gui.images.ImageHubExplorer.java Source code

Java tutorial

Introduction

Here is the source code for gui.images.ImageHubExplorer.java

Source

/**
* Hub Miner: a hubness-aware machine learning experimentation library.
* Copyright (C) 2014  Nenad Tomasev. Email: nenad.tomasev at gmail.com
* 
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package gui.images;

import data.neighbors.NeighborSetFinder;
import data.neighbors.SharedNeighborFinder;
import data.neighbors.hubness.BucketedOccDistributionGetter;
import data.neighbors.hubness.HubOrphanRegularPercentagesCalculator;
import data.neighbors.hubness.HubnessExtremesGrabber;
import data.neighbors.hubness.HubnessSkewAndKurtosisExplorer;
import data.neighbors.hubness.HubnessAboveThresholdExplorer;
import data.neighbors.hubness.KNeighborEntropyExplorer;
import data.representation.DataInstance;
import data.representation.DataSet;
import data.representation.images.color.ColorHistogramVector;
import data.representation.images.sift.LFeatRepresentation;
import distances.primary.CombinedMetric;
import distances.secondary.LocalScalingCalculator;
import distances.secondary.MutualProximityCalculator;
import distances.secondary.NICDMCalculator;
import distances.secondary.snd.SharedNeighborCalculator;
import draw.basic.BoxBlur;
import draw.basic.ColorPalette;
import draw.basic.ScreenImage;
import draw.charts.PieRenderer;
import edu.uci.ics.jung.algorithms.layout.CircleLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.graph.DirectedGraph;
import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.util.Context;
import edu.uci.ics.jung.graph.util.EdgeType;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.PickingGraphMousePlugin;
import edu.uci.ics.jung.visualization.control.PluggableGraphMouse;
import edu.uci.ics.jung.visualization.picking.PickedState;
import ioformat.FileUtil;
import ioformat.IOARFF;
import ioformat.images.ConvertJPGToPGM;
import ioformat.images.SiftUtil;
import java.awt.Color;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Vector;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import learning.supervised.Classifier;
import learning.supervised.interfaces.DistMatrixUserInterface;
import learning.supervised.interfaces.NeighborPointsQueryUserInterface;
import learning.supervised.methods.knn.AKNN;
import learning.supervised.methods.knn.DWHFNN;
import learning.supervised.methods.knn.FNN;
import learning.supervised.methods.knn.HwKNN;
import learning.supervised.methods.knn.HIKNN;
import learning.supervised.methods.knn.KNN;
import learning.supervised.methods.knn.NHBNN;
import data.neighbors.NSFUserInterface;
import images.mining.codebook.GenericCodeBook;
import ioformat.images.OpenCVFeatureIO;
import java.awt.HeadlessException;
import java.io.IOException;
import learning.supervised.methods.knn.NWKNN;
import mdsj.MDSJ;
import org.apache.commons.collections15.Predicate;
import org.apache.commons.collections15.Transformer;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PiePlot3D;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.StackedBarRenderer;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.util.Rotation;
import util.ArrayUtil;
import util.AuxSort;
import util.BasicMathUtil;

/**
 * This GUI was made with the intention of helping with analyzing the hubness of
 * the data and image data in particular, in terms of the skewed distribution of
 * implied relevance stemming from a particular choice of metric and feature
 * representation. It offers the users a choice between many standard primary
 * metrics and a set of state-of-the-art secondary metrics for hubness-aware
 * metric learning in order to improve the semantic consistency of between-image
 * similarities. It is composed of many visualization components for different
 * types of data overviews, with an emphasis on kNN set structure and hub
 * analysis. When using it, make sure to cite the following paper: Image Hub
 * Explorer: Evaluating Representations and Metrics for Content-based Image
 * Retrieval and Object Recognition, Nenad Tomasev and Dunja Mladenic, 2013,
 * ECML/PKDD conference.
 *
 * @author Nenad Tomasev <nenad.tomasev at gmail.com>
 */
public class ImageHubExplorer extends javax.swing.JFrame {

    // The workspace directory.
    private File workspace;
    // The current working directory.
    private File currentDirectory;
    private static final int PRIMARY_METRIC = 0;
    private static final int SECONDARY_METRIC = 1;
    private ImageHubExplorer frameReference = this;
    // Codebook data structures for visual word analysis.
    private GenericCodeBook codebook = null;
    private double[][] codebookProfiles = null;
    private float[] codebookGoodness = null;
    // Whether to also load the secondary distances from the disk (if available)
    // or to calculate them inside Image Hub Explorer. If the latter is selected
    // then the metric object is also available for external searches.
    private boolean secondaryLoadFlag = false;
    // This flag informs the methods that the system is currently calculating
    // something, so the user is prevented from executing certain actions.
    private volatile boolean busyCalculating = false;
    // Image data that is being analyzed.
    private BufferedImage[] images;
    // Thumbnails of the images for visualization.
    private ArrayList<BufferedImage> thumbnails;
    // Reverse neighbor sets for all the images for all the neighborhood sizes.
    private ArrayList<Integer>[][] rnnSetsAllK;
    // Neighbor occurrence profiles for all neighborhood sizes.
    private float[][][] occurrenceProfilesAllK;
    // Quantized data representation, if a representation is available. It is
    // possible to operate based on the loaded distances alone, if that is
    // necessary in the context of analysis.
    private DataSet quantizedRepresentation;
    // Number of classes in the data.
    private int numClasses;
    // Colors to use for different classes.
    private Color[] classColors;
    // Names of different classes.
    private String[] classNames;
    // Files containing primary and secondary distance matrices.
    private File primaryDMatFile;
    private File secondaryDMatFile;
    // Primary distance matrix.
    private float[][] distMatrixPrimary;
    // kNN sets in the primary distance.
    private NeighborSetFinder nsfPrimary;
    // Secondary distance matrix.
    private float[][] distMatrixSecondary;
    // kNN sets in the secondary distance.
    private NeighborSetFinder nsfSecondary;
    // CombinedMetric objects for distance calculations.
    private CombinedMetric primaryCMet = null;
    private CombinedMetric secondaryCMet = null;
    // We keep track of the selection history here.
    private ArrayList<Integer> selectedImageHistory;
    // This is the index of the current selection in the history. If we are
    // going back and forth, we move this index and the user can easily browse
    // through the browsing history.
    private int selectedImageIndexInHistory = 0;
    // These maps map the paths to specific instance indexes.
    private HashMap<String, Integer> pathIndexMap = null;
    private HashMap<String, Integer> pathIndexMapThumbnail = null;
    // Lists of image paths and image thumbnail paths.
    private ArrayList<String> imgPaths = null;
    private ArrayList<String> imgThumbPaths = null;
    // The current neighborhood size.
    private volatile int neighborhoodSize = 5;
    // Statistics related to the kNN topology and the hubness of the data.
    // Percentage of points that occur at least once as neighbors, over all
    // neighborhood sizes.
    private float[] aboveZeroArray = null;
    // Neighbor occurrence distribution skewness, over all neighborhood sizes.
    private float[] skewArray = null;
    // Neighbor occurrence distribution kurtosis, over all neighborhood sizes.
    private float[] kurtosisArray = null;
    // Highest neighbor occurrence counts, over all neighborhood sizes.
    private float[][] highestHubnesses = null;
    // Indexes of top hubs in the data, over all neighborhood sizes.
    private int[][] highestHubIndexes = null;
    // kNN set entropies, over all neighborhood sizes.
    private float[] kEntropies = null;
    // Reverse kNN set entropies, over all neighborhood sizes.
    private float[] reverseKNNEntropies = null;
    // kNN set entropy skewness, over all neighborhood sizes.
    private float[] kEntropySkews = null;
    // Reverse kNN set entropy skews, over all neighborhood sizes.
    private float[] reverseKNNEntropySkews = null;
    // Label mismatch percentages in kNN sets, over all neighborhood sizes.
    private float[] badHubnessArray = null;
    // Global class to class hubness, over all neighborhood sizes.
    private float[][][] globalClassToClasshubness = null;
    // Percentages of points that are hubs, over all neighborhood sizes.
    private float[] hubPercs = null;
    // Percentages of points that are orphans, over all neighborhood sizes.
    private float[] orphanPercs = null;
    // Percentages of points that are regular points, over all neighborhood
    // sizes.
    private float[] regularPercs = null;
    // Occurrence distributions as histograms with a fixed bucket width.
    private int[][] bucketedOccurrenceDistributions = null;
    // The current query image.
    private BufferedImage queryImage;
    // The representation of the current query.
    private DataInstance queryImageRep;
    // Local image features of the current query, if available.
    private LFeatRepresentation queryImageLFeat;
    // Neighbors of the current query image.
    private int[] queryImageNeighbors;
    // Distances to the neighbors of the current query image.
    private float[] queryImageNeighborDists;
    // Neighborhood size used for the current query.
    private int kQuery = 10;
    // Whether the kNN stats have already been calculated or not.
    private boolean neighborStatsCalculated = false;
    // Whether the classifier models have already been trained or not.
    private boolean trainedModels = false;
    // A list of classifiers.
    private Classifier[] classifiers;
    // A list of classifier names for display, as these may differ from the
    // implementaiton names.
    private String[] classifierNameList = { "kNN", "FNN", "NWKNN", "AKNN", "hw-kNN", "h-FNN", "HIKNN", "NHBNN" };
    // Lists of top hub, good hub and bad hub indexes for each class for all
    // neighborhood sizes.
    private ArrayList<Integer>[][] classTopHubLists;
    private ArrayList<Integer>[][] classTopGoodHubsList;
    private ArrayList<Integer>[][] classTopBadHubsList;
    // Corresponding occurrence frequencies to the above lists.
    private ArrayList<Integer>[][] classHubnessArrValues;
    private ArrayList<Integer>[][] classHubnessArrGoodValues;
    private ArrayList<Integer>[][] classHubnessArrBadValues;
    // A list of image indexes that belongs to each particular class.
    private ArrayList<Integer>[] classImageIndexes = new ArrayList[numClasses];
    // Point type distributions for all classes.
    private float[][] classPTypes;
    // Per-class visualizations of the influence of hubness.
    private ClassHubsPanel[] classStatsOverviews;
    // Image coordinates for the MDS screen.
    private float[][] imageCoordinatesXY;
    // Number of images to show in the MDS screen.
    private int numImagesDrawn = 300;
    // Maximum and minimum display scale.
    private int maxImageScale = 80;
    private int minImageScale = 10;
    // Calculated MDS landscapes. Calculating a landscape takes some time, so
    // this helps with quickly changing the background when the neighborhood
    // size is changed, as previously calculated backgrounds are then shown
    // instead of being re-calculated.
    private BufferedImage[] mdsBackgrounds;
    // kNN graphs for all the k-values.
    private DirectedGraph<ImageNode, NeighborLink>[] neighborGraphs;
    private ArrayList<Integer> vertexIndexes;
    private ArrayList<ImageNode> vertices;
    private ArrayList<NeighborLink>[] edges;
    private HashMap<Integer, ImageNode> verticesHash;
    private HashMap<Integer, Integer> verticesNodeIndexHash;
    private VisualizationViewer[] graphVServers;
    // File containing the codebook profile for visual word utility assessment.
    File codebookProfileFile = null;
    // Panels for codebook vector profiles.
    CodebookVectorProfilePanel[] codebookProfPanels;

    /**
     * Nodes for kNN graph visualizations.
     */
    static class ImageNode {

        int id;
        ImageIcon icon;
        String thumbPath;

        /**
         * Initialization.
         *
         * @param id Integer that is the node ID and will correspond to the
         * index of the image in the representation.
         * @param icon ImageIcon that is the image thumbnail.
         * @param thumbPath String that is the thumbnail path.
         */
        public ImageNode(int id, ImageIcon icon, String thumbPath) {
            this.icon = icon;
            this.id = id;
            this.thumbPath = thumbPath;
        }

        /**
         * @return ImageIcon that is the image thumbnail.
         */
        public Icon getIcon() {
            return icon;
        }

        @Override
        public String toString() {
            return thumbPath;
        }
    }

    /**
     * Edges for kNN graph visualization.
     */
    static class NeighborLink {

        // The total number of edges.
        private static int edgeCount = 0;
        // Edge weight.
        double weight;
        // Edge ID.
        int id;
        // Source and target ImageNode that are connected due to the neighbor
        // relation.
        ImageNode source, target;

        /**
         * Initialization.
         *
         * @param weight Double that is the edge weight.
         * @param source ImageNode that is the source vertex for this edge.
         * @param target ImageNode that is the target vertex for this edge.
         */
        public NeighborLink(double weight, ImageNode source, ImageNode target) {
            this.id = edgeCount++;
            this.weight = weight;
            this.source = source;
            this.target = target;
        }

        @Override
        public String toString() {
            return " " + BasicMathUtil.makeADecimalCutOff(weight, 3);
        }

        /**
         * @return ImageNode that is the source vertex for this edge.
         */
        public ImageNode getSource() {
            return source;
        }

        /**
         * @return ImageNode that is the target vertex for this edge.
         */
        public ImageNode getTarget() {
            return target;
        }
    }

    /**
     * This method deletes and resets all the kNN graphs.
     */
    private void graphsDelete() {
        if (neighborGraphs != null) {
            for (int i = 0; i < neighborGraphs.length; i++) {
                neighborGraphs[i] = null;
                graphVServers[i] = null;
            }
        }
        neighborGraphs = null;
        vertexIndexes = null;
        vertices = null;
        edges = null;
        neighborGraphScrollPane.setViewportView(null);
        neighborGraphScrollPane.revalidate();
        neighborGraphScrollPane.repaint();
        System.gc();
    }

    /**
     * This method removes the currently selected image from the kNN graph
     * visualizations for all neighborhood sizes.
     */
    private void removeSelectedImageFromGraph() {
        if (neighborStatsCalculated && neighborGraphs != null && selectedImageHistory != null
                && selectedImageHistory.size() > 0) {
            int index = selectedImageHistory.get(selectedImageIndexInHistory);
            if (verticesHash.containsKey(index)) {
                ImageNode delVertex = verticesHash.get(index);
                // Create two lists of edges, those that need to be deleted and
                // those that will be retained. Create one such list for each
                // neighborhood size.
                ArrayList<NeighborLink>[] retainedEdges;
                ArrayList<NeighborLink>[] discardedEdges;
                retainedEdges = new ArrayList[50];
                discardedEdges = new ArrayList[50];
                for (int kTmp = 0; kTmp < 50; kTmp++) {
                    retainedEdges[kTmp] = new ArrayList<>(500);
                    discardedEdges[kTmp] = new ArrayList<>(500);
                    for (int i = 0; i < edges[kTmp].size(); i++) {
                        if (edges[kTmp].get(i) != null) {
                            if (delVertex.equals(edges[kTmp].get(i).getSource())
                                    || delVertex.equals(edges[kTmp].get(i).getTarget())) {
                                // Discard the edge.
                                discardedEdges[kTmp].add(edges[kTmp].get(i));
                            } else {
                                // Keep the edge.
                                retainedEdges[kTmp].add(edges[kTmp].get(i));
                            }
                        }
                    }
                    // Update the internal edge lists.
                    edges[kTmp] = retainedEdges[kTmp];
                    // Remove the removed edges from the graphs.
                    for (int i = 0; i < discardedEdges[kTmp].size(); i++) {
                        neighborGraphs[kTmp].removeEdge(discardedEdges[kTmp].get(i));
                    }
                    neighborGraphs[kTmp].removeVertex(delVertex);
                    graphVServers[kTmp].revalidate();
                    graphVServers[kTmp].repaint();
                }
                verticesHash.remove(index);
            }
            // Update the graphical components.
            neighborGraphScrollPane.revalidate();
            neighborGraphScrollPane.repaint();
        }
    }

    /**
     * Add a list of images to the kNN graph visualizations.
     *
     * @param indexes ArrayList<Integer> of image indexes to insert.
     */
    private void addSelectedImagesToGraph(ArrayList<Integer> indexes) {
        int[] aIndexes = new int[0];
        if (indexes != null) {
            aIndexes = new int[indexes.size()];
            for (int i = 0; i < indexes.size(); i++) {
                aIndexes[i] = indexes.get(i);
            }
        }
        // Delegate to a method that operates on the index arrays.
        addSelectedImagesToGraph(aIndexes);
    }

    /**
     * Add an array of images to the kNN graph visualizations.
     *
     * @param indexes int[] of image indexes to insert.
     */
    private void addSelectedImagesToGraph(int[] indexes) {
        if (neighborStatsCalculated && neighborGraphs != null) {
            for (int index : indexes) {
                if (verticesHash.containsKey(index)) {
                    // If the image is already contained in the graphs, skip it.
                    continue;
                }
                // Create a new node to insert.
                ImageNode newVertex = new ImageNode(index, new ImageIcon(imgThumbPaths.get(index)),
                        imgThumbPaths.get(index));
                // Add the node to the vertex set.
                vertexIndexes.add(index);
                vertices.add(newVertex);
                for (int kTmp = 0; kTmp < 50; kTmp++) {
                    neighborGraphs[kTmp].addVertex(newVertex);
                    verticesHash.put(index, newVertex);
                    verticesNodeIndexHash.put(index, vertices.size() - 1);
                    graphVServers[kTmp].revalidate();
                    graphVServers[kTmp].repaint();
                    neighborGraphScrollPane.revalidate();
                    neighborGraphScrollPane.repaint();
                }
            }
            // This might be improved. All edges are removed from the graphs
            // and then inserted anew.
            for (int kTmp = 0; kTmp < 50; kTmp++) {
                for (int i = 0; i < edges[kTmp].size(); i++) {
                    neighborGraphs[kTmp].removeEdge(edges[kTmp].get(i));
                }
                edges[kTmp] = new ArrayList<>(100);
                graphVServers[kTmp].revalidate();
                graphVServers[kTmp].repaint();
                neighborGraphScrollPane.revalidate();
                neighborGraphScrollPane.repaint();
            }
            NeighborSetFinder nsf = getNSF();
            int[][] kneighbors = nsf.getKNeighbors();
            float[][] kdistances = nsf.getKDistances();
            // For all the neighborhood sizes in the range.
            for (int kTmp = 0; kTmp < 50; kTmp++) {
                for (int i = 0; i < vertices.size(); i++) {
                    // For all the neighbors.
                    for (int kN = 0; kN < kTmp + 1; kN++) {
                        if (verticesHash.containsKey(kneighbors[vertexIndexes.get(i)][kN])) {
                            NeighborLink newEdge = new NeighborLink(kdistances[vertexIndexes.get(i)][kN],
                                    vertices.get(i), verticesHash.get(kneighbors[vertexIndexes.get(i)][kN]));
                            neighborGraphs[kTmp].addEdge(newEdge, vertices.get(i),
                                    verticesHash.get(kneighbors[vertexIndexes.get(i)][kN]), EdgeType.DIRECTED);
                            edges[kTmp].add(newEdge);
                        }
                    }
                }
                graphVServers[kTmp].revalidate();
                graphVServers[kTmp].repaint();
            }
            // Determine how to display the nodes.
            Layout<ImageNode, NeighborLink> layout = new CircleLayout(neighborGraphs[neighborhoodSize - 1]);
            layout.setSize(new Dimension(500, 500));
            layout.initialize();
            VisualizationViewer<ImageNode, NeighborLink> vv = new VisualizationViewer<>(layout);
            vv.setPreferredSize(new Dimension(550, 550));
            vv.setMinimumSize(new Dimension(550, 550));
            vv.setDoubleBuffered(true);
            vv.setEnabled(true);
            graphVServers[neighborhoodSize - 1] = vv;
            vv.getRenderContext().setVertexIconTransformer(new IconTransformer<ImageNode, Icon>());
            vv.getRenderContext().setVertexShapeTransformer(new ShapeTransformer<ImageNode, Shape>());
            vv.getRenderContext().setEdgeArrowPredicate(new DirectionDisplayPredicate());
            vv.getRenderContext().setEdgeLabelTransformer(new Transformer() {
                @Override
                public String transform(Object e) {
                    return (e.toString());
                }
            });
            PluggableGraphMouse gm = new PluggableGraphMouse();
            gm.add(new PickingGraphMousePlugin());
            vv.setGraphMouse(gm);
            vv.setBackground(Color.WHITE);
            vv.setVisible(true);
            final PickedState<ImageNode> pickedState = vv.getPickedVertexState();
            pickedState.addItemListener(new ItemListener() {
                @Override
                public void itemStateChanged(ItemEvent e) {
                    Object subject = e.getItem();
                    if (subject instanceof ImageNode) {
                        ImageNode vertex = (ImageNode) subject;
                        if (pickedState.isPicked(vertex)) {
                            setSelectedImageForIndex(vertex.id);
                        } else {
                        }
                    }
                }
            });
            vv.validate();
            vv.repaint();
            neighborGraphScrollPane.setViewportView(vv);
            neighborGraphScrollPane.revalidate();
            neighborGraphScrollPane.repaint();
        }
    }

    /**
     * Inserts the currently selected image into all kNN graphs for
     * visualization.
     */
    private void addSelectedImageToGraph() {
        if (neighborStatsCalculated && neighborGraphs != null && selectedImageHistory != null
                && selectedImageHistory.size() > 0) {
            int index = selectedImageHistory.get(selectedImageIndexInHistory);
            if (verticesHash.containsKey(index)) {
                // If it is already contained within the graphs, do nothing.
                return;
            }
            // Create a new node for the image.
            ImageNode newVertex = new ImageNode(index, new ImageIcon(imgThumbPaths.get(index)),
                    imgThumbPaths.get(index));
            vertexIndexes.add(index);
            vertices.add(newVertex);
            NeighborSetFinder nsf = getNSF();
            int[][] kneighbors = nsf.getKNeighbors();
            float[][] kdistances = nsf.getKDistances();
            // For all relevant neighborhood sizes.
            for (int kTmp = 0; kTmp < 50; kTmp++) {
                neighborGraphs[kTmp].addVertex(newVertex);
                verticesHash.put(index, newVertex);
                verticesNodeIndexHash.put(index, vertices.size() - 1);
                // Re-draw all the edges.
                // This should be changed to only update the affected ones.
                for (int i = 0; i < edges[kTmp].size(); i++) {
                    neighborGraphs[kTmp].removeEdge(edges[kTmp].get(i));
                }
                for (int i = 0; i < vertices.size(); i++) {
                    for (int kN = 0; kN < kTmp; kN++) {
                        if (verticesHash.containsKey(kneighbors[vertexIndexes.get(i)][kN])) {
                            NeighborLink newEdge = new NeighborLink(kdistances[vertexIndexes.get(i)][kN],
                                    vertices.get(i), verticesHash.get(kneighbors[vertexIndexes.get(i)][kN]));
                            neighborGraphs[kTmp].addEdge(newEdge, vertices.get(i),
                                    verticesHash.get(kneighbors[vertexIndexes.get(i)][kN]), EdgeType.DIRECTED);
                            edges[kTmp].add(newEdge);
                        }
                    }
                }
                graphVServers[kTmp].revalidate();
                graphVServers[kTmp].repaint();
            }
            // Set up how the nodes will be drawn.
            Layout<ImageNode, NeighborLink> layout = new CircleLayout(neighborGraphs[neighborhoodSize - 1]);
            layout.setSize(new Dimension(500, 500));
            layout.initialize();
            VisualizationViewer<ImageNode, NeighborLink> vv = new VisualizationViewer<>(layout);
            vv.setPreferredSize(new Dimension(550, 550));
            vv.setMinimumSize(new Dimension(550, 550));
            vv.setDoubleBuffered(true);
            vv.setEnabled(true);
            graphVServers[neighborhoodSize - 1] = vv;
            vv.getRenderContext().setVertexIconTransformer(new IconTransformer<ImageNode, Icon>());
            vv.getRenderContext().setVertexShapeTransformer(new ShapeTransformer<ImageNode, Shape>());
            vv.getRenderContext().setEdgeArrowPredicate(new DirectionDisplayPredicate());
            vv.getRenderContext().setEdgeLabelTransformer(new Transformer() {
                @Override
                public String transform(Object e) {
                    return (e.toString());
                }
            });
            PluggableGraphMouse gm = new PluggableGraphMouse();
            gm.add(new PickingGraphMousePlugin());
            vv.setGraphMouse(gm);
            vv.setBackground(Color.WHITE);
            vv.setVisible(true);
            final PickedState<ImageNode> pickedState = vv.getPickedVertexState();
            pickedState.addItemListener(new ItemListener() {
                @Override
                public void itemStateChanged(ItemEvent e) {
                    Object subject = e.getItem();
                    if (subject instanceof ImageNode) {
                        ImageNode vertex = (ImageNode) subject;
                        if (pickedState.isPicked(vertex)) {
                            setSelectedImageForIndex(vertex.id);
                        }
                    }
                }
            });
            // Refresh all the display components.
            vv.revalidate();
            vv.repaint();
            neighborGraphScrollPane.setViewportView(graphVServers[neighborhoodSize - 1]);
            neighborGraphScrollPane.revalidate();
            neighborGraphScrollPane.repaint();
        }
    }

    /**
     * IconTransformer class. These class is used for node visualization in kNN
     * graphs.
     *
     * @param <ImageNode> Image node.
     * @param <Icon> Corresponding icon.
     */
    class IconTransformer<ImageNode, Icon> implements Transformer<ImageNode, Icon> {

        public static final int ICON_SIZE = 30;

        /**
         * @return Integer that is the shape height.
         */
        public int getHeight() {
            return (ICON_SIZE);
        }

        /**
         * @return Integer that is the shape width.
         */
        public int getWidth() {
            return (ICON_SIZE);
        }

        @Override
        public Icon transform(ImageNode node) {
            if (node != null) {
                Icon icon = (Icon) (new ImageIcon(node.toString()));
                return icon;
            } else {
                return null;
            }
        }
    }

    /**
     * ShapeTransformer class. These class is used for node visualization in kNN
     * graphs.
     *
     * @param <ImageNode> Image node.
     * @param <Shape> Corresponding Shape.
     */
    class ShapeTransformer<ImageNode, Shape> implements Transformer<ImageNode, Shape> {

        public static final int ICON_SIZE = 30;

        /**
         * @return Integer that is the shape height.
         */
        public int getHeight() {
            return (ICON_SIZE);
        }

        /**
         * @return Integer that is the shape width.
         */
        public int getWidth() {
            return (ICON_SIZE);
        }

        @Override
        public Shape transform(ImageNode node) {
            if (node != null) {
                ImageIcon icon = new ImageIcon(node.toString());
                int width = icon.getIconWidth();
                int height = icon.getIconHeight();
                Rectangle2D shape = new Rectangle2D.Float(-width / 2, -height / 2, width, height);
                return (Shape) shape;
            } else {
                return null;
            }
        }
    }

    /**
     * Handler for directed and undirected edges for kNN graph visualization.
     *
     * @param <V> Vertex type.
     * @param <E> Edge type.
     */
    private final static class DirectionDisplayPredicate<V, E> implements Predicate<Context<Graph<V, E>, E>> {

        /**
         * The default constructor.
         */
        public DirectionDisplayPredicate() {
        }

        @Override
        public boolean evaluate(Context<Graph<V, E>, E> context) {
            Graph<V, E> graph = context.graph;
            E edge = context.element;
            if (graph.getEdgeType(edge) == EdgeType.DIRECTED) {
                return true;
            }
            if (graph.getEdgeType(edge) == EdgeType.UNDIRECTED) {
                return true;
            }
            return true;
        }
    }

    /**
     * Initialize all the kNN graphs for visualization.
     */
    private void graphsInit() {
        neighborGraphs = new DirectedGraph[50];
        graphVServers = new VisualizationViewer[50];
        edges = new ArrayList[50];
        // For all the relevant neighborhood sizes.
        for (int kTmp = 0; kTmp < 50; kTmp++) {
            // Create a new graph.
            DirectedGraph graph = new DirectedSparseMultigraph<>();
            neighborGraphs[kTmp] = graph;
            Layout<ImageNode, NeighborLink> layout = new CircleLayout(neighborGraphs[kTmp]);
            layout.setSize(new Dimension(500, 500));
            // Set the rendering specification.
            VisualizationViewer<ImageNode, NeighborLink> vv = new VisualizationViewer<>(layout);
            vv.setPreferredSize(new Dimension(550, 550));
            vv.setMinimumSize(new Dimension(550, 550));
            vv.setDoubleBuffered(true);
            vv.setEnabled(true);
            graphVServers[kTmp] = vv;
            vv.getRenderContext().setVertexIconTransformer(new IconTransformer<ImageNode, Icon>());
            vv.getRenderContext().setVertexShapeTransformer(new ShapeTransformer<ImageNode, Shape>());
            vv.getRenderContext().setEdgeArrowPredicate(new DirectionDisplayPredicate());
            vv.getRenderContext().setEdgeLabelTransformer(new Transformer() {
                @Override
                public String transform(Object e) {
                    return (e.toString());
                }
            });
            PluggableGraphMouse gm = new PluggableGraphMouse();
            gm.add(new PickingGraphMousePlugin());
            vv.setGraphMouse(gm);
            vv.setBackground(Color.WHITE);
            vv.setVisible(true);
            final PickedState<ImageNode> pickedState = vv.getPickedVertexState();
            // Add the selection listeners.
            pickedState.addItemListener(new ItemListener() {
                @Override
                public void itemStateChanged(ItemEvent e) {
                    Object subject = e.getItem();
                    if (subject instanceof ImageNode) {
                        ImageNode vertex = (ImageNode) subject;
                        if (pickedState.isPicked(vertex)) {
                            setSelectedImageForIndex(vertex.id);
                        }
                    }
                }
            });
        }
        verticesHash = new HashMap<>(500);
        verticesNodeIndexHash = new HashMap<>(500);
        vertexIndexes = new ArrayList<>(200);
        vertices = new ArrayList<>(200);
        edges = new ArrayList[50];
        for (int kTmp = 0; kTmp < 50; kTmp++) {
            edges[kTmp] = new ArrayList<>(500);
        }
        // Refresh the display.
        graphVServers[neighborhoodSize - 1].revalidate();
        graphVServers[neighborhoodSize - 1].repaint();
        neighborGraphScrollPane.setViewportView(graphVServers[neighborhoodSize - 1]);
        neighborGraphScrollPane.setVisible(true);
        neighborGraphScrollPane.revalidate();
        neighborGraphScrollPane.repaint();
    }

    /**
     * Train all the classifier models.
     */
    public void trainModels() {
        if (busyCalculating) {
            // If the system is already working on something, this call will be
            // ignored.
            return;
        }
        busyCalculating = true;
        try {
            trainedModels = true;
            classifiers = new Classifier[classifierNameList.length];
            // Get the current metric context.
            CombinedMetric cmet = getCombinedMetric();
            // Initialize all the classifiers.
            classifiers[0] = new KNN(kQuery, cmet);
            classifiers[1] = new FNN(kQuery, cmet, numClasses);
            classifiers[2] = new NWKNN(kQuery, cmet, numClasses);
            classifiers[3] = new AKNN(kQuery, cmet, numClasses);
            classifiers[4] = new HwKNN(numClasses, cmet, kQuery);
            classifiers[5] = new DWHFNN(kQuery, cmet, numClasses);
            classifiers[6] = new HIKNN(kQuery, cmet, numClasses);
            classifiers[7] = new NHBNN(kQuery, cmet, numClasses);
            // Get the current distance matrix.
            float[][] distances = getDistances();
            // Get the current kNN sets.
            NeighborSetFinder nsf = getNSF();
            System.out.println("Training classifier models.");
            ArrayList<Integer> completeDataArray = new ArrayList<>(quantizedRepresentation.size());
            for (int i = 0; i < quantizedRepresentation.size(); i++) {
                completeDataArray.add(i);
            }
            for (int i = 0; i < classifiers.length; i++) {
                // Set the data.
                classifiers[i].setDataIndexes(completeDataArray, quantizedRepresentation);
                if (classifiers[i] instanceof DistMatrixUserInterface) {
                    // If the classifier requires the distance matrix, set the
                    // distance matrix.
                    ((DistMatrixUserInterface) (classifiers[i])).setDistMatrix(distances);
                }
                if (classifiers[i] instanceof NSFUserInterface) {
                    // If the classifier requires kNN sets, set the kNN sets.
                    ((NSFUserInterface) (classifiers[i])).setNSF(nsf);
                }
                try {
                    classifiers[i].train();
                } catch (Exception e) {
                    System.err.println(e.getMessage());
                }
            }
            System.out.println("Models trained.");
            JOptionPane.showMessageDialog(frameReference, "Models trained.");
        } catch (Exception e) {
            System.err.println(e.getMessage());
        } finally {
            // Report that the calculations have been finished.
            busyCalculating = false;
        }
    }

    /**
     * Sets the currently selected image to be the query image in the query
     * panel.
     */
    public void setQueryImageFromCollection() {
        if (busyCalculating) {
            // If the system is already working on something, this call will be
            // ignored.
            return;
        }
        if (selectedImageHistory == null || selectedImageIndexInHistory >= selectedImageHistory.size()) {
            return;
        }
        try {
            int index = selectedImageHistory.get(selectedImageIndexInHistory);
            queryImage = getPhoto(index);
            // Set the image to the query image panel.
            queryImagePanel.setImage(queryImage);
            queryImagePanel.revalidate();
            queryImagePanel.repaint();
            if (quantizedRepresentation != null) {
                // Use the existing representation, if available.
                queryImageRep = quantizedRepresentation.getInstance(index);
            }
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }

    /**
     * This method queries the image dataset by a single specified image.
     */
    private void imageQuery() {
        if (busyCalculating) {
            // If the system is already working on something, this call will be
            // ignored.
            return;
        }
        try {
            busyCalculating = true;
            // Get the k-nearest neighbors.
            NeighborSetFinder nsf = getNSF();
            queryImageNeighbors = NeighborSetFinder.getIndexesOfNeighbors(quantizedRepresentation, queryImageRep,
                    Math.min(kQuery, nsf.getKNeighbors()[0].length), getCombinedMetric());
            CombinedMetric cmet = getCombinedMetric();
            queryImageNeighborDists = new float[queryImageNeighbors.length];
            // Get the distances to the neighbors.
            for (int i = 0; i < queryImageNeighbors.length; i++) {
                queryImageNeighborDists[i] = cmet.dist(queryImageRep,
                        quantizedRepresentation.getInstance(queryImageNeighbors[i]));
            }
            queryNNPanel.removeAll();
            queryNNPanel.revalidate();
            queryNNPanel.repaint();
            // Add all the kNN-s of the query to the query panel.
            for (int i = 0; i < queryImageNeighbors.length; i++) {
                BufferedImage thumb = thumbnails.get(queryImageNeighbors[i]);
                ImagePanelWithClass imgPan = new ImagePanelWithClass(classColors);
                imgPan.addMouseListener(new NeighborSelectionListener());
                imgPan.setImage(thumb, quantizedRepresentation.getLabelOf(queryImageNeighbors[i]),
                        queryImageNeighbors[i]);
                queryNNPanel.add(imgPan);
            }
            queryNNPanel.revalidate();
            queryNNPanel.repaint();
            // If the classifier models are available, get some predictions and
            // display them to the user.
            if (trainedModels) {
                System.out.println("Classifying.");
                classifierPredictionsPanel.removeAll();
                classifierPredictionsPanel.revalidate();
                classifierPredictionsPanel.repaint();
                // Calculate the distances to the remaining points.
                float[] trainingDists = new float[quantizedRepresentation.size()];
                for (int i = 0; i < queryImageNeighbors.length; i++) {
                    trainingDists[queryImageNeighbors[i]] = queryImageNeighborDists[i];
                }
                for (int i = 0; i < classifiers.length; i++) {
                    System.out.println("Classification by" + classifierNameList[i]);
                    // Class affiliation prediction.
                    float[] prediction;
                    if (classifiers[i] instanceof NeighborPointsQueryUserInterface) {
                        // Get the prediction
                        prediction = ((NeighborPointsQueryUserInterface) (classifiers[i]))
                                .classifyProbabilistically(queryImageRep, trainingDists, queryImageNeighbors);
                    } else {
                        // Get the prediction.
                        prediction = classifiers[i].classifyProbabilistically(queryImageRep);
                    }
                    ClassifierResultPanel cResPanel = new ClassifierResultPanel();
                    cResPanel.setResults(prediction, classifierNameList[i], classColors, classNames);
                    classifierPredictionsPanel.add(cResPanel);
                }
                classifierPredictionsPanel.revalidate();
                classifierPredictionsPanel.repaint();
            }
        } catch (Exception e) {
            System.err.println(e.getMessage());
        } finally {
            busyCalculating = false;
        }
    }

    /**
     * Sets all the neighbor stats for the currently selected k-value.
     *
     * @param currentK Integer that is the current neighborhood size.
     */
    public synchronized void setStatFieldsForK(int currentK) {
        // Percentage of elements that occur at least ones.
        if (aboveZeroArray != null) {
            percAboveLabelValue.setText(
                    (new Float(BasicMathUtil.makeADecimalCutOff(aboveZeroArray[currentK - 1], 2))).toString());
        } else {
            percAboveLabelValue.setText("...");
        }
        // Neighbor occurrence distribution skewness.
        if (skewArray != null) {
            skewnessLabelValue
                    .setText((new Float(BasicMathUtil.makeADecimalCutOff(skewArray[currentK - 1], 2))).toString());
        } else {
            skewnessLabelValue.setText("...");
        }
        // Neighbor occurrence distribution kurtosis.
        if (kurtosisArray != null) {
            kurtosisLabelValue.setText(
                    (new Float(BasicMathUtil.makeADecimalCutOff(kurtosisArray[currentK - 1], 2))).toString());
        } else {
            kurtosisLabelValue.setText("...");
        }
        // Highest neighbor occurrence frequencies.
        if (highestHubnesses != null) {
            majorDegLabelValue.setText((new Float(BasicMathUtil.makeADecimalCutOff(
                    highestHubnesses[currentK - 1][highestHubnesses[currentK - 1].length - 1], 2))).toString());
        } else {
            majorDegLabelValue.setText("...");
        }
        // kNN set entropies.
        if (kEntropies != null) {
            nkEntropySkewnessValues.setText(
                    (new Float(BasicMathUtil.makeADecimalCutOff(kEntropySkews[currentK - 1], 2))).toString());
            nkEntropyLabelValue
                    .setText((new Float(BasicMathUtil.makeADecimalCutOff(kEntropies[currentK - 1], 2))).toString());
        } else {
            nkEntropyLabelValue.setText("...");
            nkEntropySkewnessValues.setText("...");
        }
        // Reverse kNN set entropies.
        if (reverseKNNEntropies != null) {
            rnkEntropySkewnessValue
                    .setText((new Float(BasicMathUtil.makeADecimalCutOff(reverseKNNEntropySkews[currentK - 1], 2)))
                            .toString());
            rnkEntropyValue.setText(
                    (new Float(BasicMathUtil.makeADecimalCutOff(reverseKNNEntropies[currentK - 1], 2))).toString());
        } else {
            rnkEntropyValue.setText("...");
            rnkEntropySkewnessValue.setText("...");
        }
        // Bad hubness percentages as percentages of label mismatches in kNN
        // sets.
        if (badHubnessArray != null) {
            badHubnessLabelValue.setText(
                    (new Float(BasicMathUtil.makeADecimalCutOff(badHubnessArray[currentK - 1], 2))).toString());
        } else {
            badHubnessLabelValue.setText("...");
        }
        // Percentage of points that are hubs.
        if (hubPercs != null) {
            hubsLabelValue
                    .setText((new Float(BasicMathUtil.makeADecimalCutOff(hubPercs[currentK - 1], 2))).toString());
        } else {
            hubsLabelValue.setText("...");
        }
        // Percentage of points that are orphans.
        if (orphanPercs != null) {
            orphansLabelValue.setText(
                    (new Float(BasicMathUtil.makeADecimalCutOff(orphanPercs[currentK - 1], 2))).toString());
        } else {
            orphansLabelValue.setText("...");
        }
        // Percentage of points that are regular points.
        if (regularPercs != null) {
            regularLabelValue.setText(
                    (new Float(BasicMathUtil.makeADecimalCutOff(regularPercs[currentK - 1], 2))).toString());
        } else {
            regularLabelValue.setText("...");
        }
        // Refresh the display.
        percAboveLabelValue.revalidate();
        percAboveLabelValue.repaint();
        skewnessLabelValue.revalidate();
        skewnessLabelValue.repaint();
        kurtosisLabelValue.revalidate();
        kurtosisLabelValue.repaint();
        majorDegLabelValue.revalidate();
        majorDegLabelValue.repaint();
        nkEntropySkewnessValues.revalidate();
        nkEntropySkewnessValues.repaint();
        nkEntropyLabelValue.revalidate();
        nkEntropyLabelValue.repaint();
        rnkEntropySkewnessValue.revalidate();
        rnkEntropySkewnessValue.repaint();
        rnkEntropyValue.revalidate();
        rnkEntropyValue.repaint();
        badHubnessLabelValue.revalidate();
        badHubnessLabelValue.repaint();
        hubsLabelValue.revalidate();
        hubsLabelValue.repaint();
        orphansLabelValue.revalidate();
        orphansLabelValue.repaint();
        regularLabelValue.revalidate();
        regularLabelValue.repaint();
        // Now generate the occurrence frequency distribution chart, discretized
        // to fixed-length buckets.
        DefaultCategoryDataset hDistDataset = new DefaultCategoryDataset();
        for (int i = 0; i < bucketedOccurrenceDistributions[neighborhoodSize - 1].length; i++) {
            hDistDataset.addValue(bucketedOccurrenceDistributions[neighborhoodSize - 1][i], "Number of Examples",
                    i + "");
        }
        JFreeChart chart = ChartFactory.createBarChart("Occurrence Frequency Distribution", "", "", hDistDataset,
                PlotOrientation.VERTICAL, false, true, false);
        ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setPreferredSize(new Dimension(440, 180));
        chartHoldingPanelOccDistribution.removeAll();
        chartHoldingPanelOccDistribution.add(chartPanel);
        chartHoldingPanelOccDistribution.revalidate();
        chartHoldingPanelOccDistribution.repaint();
        // Calculate class to class hubness.
        for (int c1 = 0; c1 < numClasses; c1++) {
            for (int c2 = 0; c2 < numClasses; c2++) {
                classHubnessTable.setValueAt(globalClassToClasshubness[currentK - 1][c1][c2], c1, c2);
            }
        }
        classHubnessTable.setDefaultRenderer(Object.class,
                new ClassToClassHubnessMatrixRenderer(globalClassToClasshubness[currentK - 1], numClasses));
    }

    /**
     * Neighbor selection listener.
     */
    class NeighborSelectionListener implements MouseListener {

        @Override
        public void mousePressed(MouseEvent e) {
            Component comp = e.getComponent();
            if (comp instanceof ImagePanelWithClass) {
                int index = ((ImagePanelWithClass) comp).getImageIndex();
                setSelectedImageForIndex(index);
            }
        }

        @Override
        public void mouseReleased(MouseEvent e) {
        }

        @Override
        public void mouseEntered(MouseEvent e) {
        }

        @Override
        public void mouseExited(MouseEvent e) {
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            Component comp = e.getComponent();
            if (comp instanceof ImagePanelWithClass) {
                int index = ((ImagePanelWithClass) comp).getImageIndex();
                setSelectedImageForIndex(index);
            }
        }
    }

    /**
     * This class handles adding neighbors and reverse neighbors to their
     * panels.
     */
    private class SetImageNeighborsHelper implements Runnable {

        private BufferedImage thumb;
        private int label, index;
        private JPanel panel;

        /**
         * Initialization.
         *
         * @param panel Panel to add the image to.
         * @param thumb Thumbnail of the image to add.
         * @param label Label of the image to add.
         * @param index Integer that is the index of the image to add.
         */
        public SetImageNeighborsHelper(JPanel panel, BufferedImage thumb, int label, int index) {
            this.thumb = thumb;
            this.label = label;
            this.index = index;
            this.panel = panel;
        }

        @Override
        public void run() {
            ImagePanelWithClass rrneighbor = new ImagePanelWithClass(classColors);
            rrneighbor.addMouseListener(new NeighborSelectionListener());
            rrneighbor.setImage(thumb, label, index);
            panel.add(rrneighbor);
        }
    }

    /**
     * This method sets the image of the specified index as the currently
     * selected image and updates all the views.
     *
     * @param index Integer that is the index of the image to select as the
     * current image.
     */
    private synchronized void setSelectedImageForIndex(int index) {
        try {
            // Update the selected image panels.
            BufferedImage photo = getPhoto(index);
            selectedImagePanelClassNeighborMain.setImage(photo);
            selectedImagePanelClassNeighbor.setImage(photo);
            selectedImagePanelClass.setImage(photo);
            selectedImagePanelSearch.setImage(photo);
            String shortPath = imgPaths.get(index).substring(workspace.getPath().length(),
                    imgPaths.get(index).length());
            // Update the labels with the new name.
            selectedImagePathLabelClassNeighborMain.setText(shortPath);
            selectedImagePathLabelClassNeighbor.setText(shortPath);
            selectedImagePathLabelClass.setText(shortPath);
            selectedImagePathLabelSearch.setText(shortPath);
            // Update the class colors.
            selectedImageLabelClassNeighborMain
                    .setBackground(classColors[quantizedRepresentation.getLabelOf(index)]);
            selectedImageLabelClassNeighbor.setBackground(classColors[quantizedRepresentation.getLabelOf(index)]);
            selectedImageLabelClass.setBackground(classColors[quantizedRepresentation.getLabelOf(index)]);
            selectedImageLabelSearch.setBackground(classColors[quantizedRepresentation.getLabelOf(index)]);
            // Refresh the display.
            selectedImageLabelClassNeighborMain.setOpaque(true);
            selectedImageLabelClassNeighbor.setOpaque(true);
            selectedImageLabelClass.setOpaque(true);
            selectedImageLabelSearch.setOpaque(true);
            selectedImageLabelClassNeighborMain.repaint();
            selectedImageLabelClassNeighbor.repaint();
            selectedImageLabelClass.repaint();
            selectedImageLabelSearch.repaint();
            // Update the history.
            if (selectedImageHistory == null) {
                selectedImageHistory = new ArrayList<>(200);
                selectedImageIndexInHistory = -1;
            }
            // Discard the future history.
            if (selectedImageIndexInHistory < selectedImageHistory.size() - 1) {
                for (int i = selectedImageHistory.size() - 1; i > selectedImageIndexInHistory; i--) {
                    selectedImageHistory.remove(i);
                }
            }
            selectedImageHistory.add(index);
            selectedImageIndexInHistory = selectedImageHistory.size() - 1;
            // Update the nearest neighbors and the reverse nearest neighbors.
            NeighborSetFinder nsf = getNSF();
            nnPanel.removeAll();
            rnnPanel.removeAll();
            nnPanel.revalidate();
            nnPanel.repaint();
            rnnPanel.revalidate();
            rnnPanel.repaint();
            int[][] kneighbors = nsf.getKNeighbors();
            for (int neighborIndex = 0; neighborIndex < neighborhoodSize; neighborIndex++) {
                // Insert all the nearest neighbors to their panel.
                BufferedImage thumb = thumbnails.get(kneighbors[index][neighborIndex]);
                try {
                    Thread t = new Thread(new SetImageNeighborsHelper(nnPanel, thumb,
                            quantizedRepresentation.getLabelOf(kneighbors[index][neighborIndex]),
                            kneighbors[index][neighborIndex]));
                    t.start();
                    t.join(500);
                    if (t.isAlive()) {
                        t.interrupt();
                    }
                } catch (Throwable thr) {
                    System.err.println(thr.getMessage());
                }
            }
            // Insert all the reverse nearest neighbors to their panel.
            ArrayList<Integer>[] rrns = null;
            if (rnnSetsAllK != null) {
                rrns = rnnSetsAllK[neighborhoodSize - 1];
            }
            if (rrns != null && rrns[index] != null && rrns[index].size() > 0) {
                for (int i = 0; i < rrns[index].size(); i++) {
                    BufferedImage thumb = thumbnails.get(rrns[index].get(i));
                    try {
                        Thread t = new Thread(new SetImageNeighborsHelper(rnnPanel, thumb,
                                quantizedRepresentation.getLabelOf(rrns[index].get(i)), rrns[index].get(i)));
                        t.start();
                        t.join(500);
                        if (t.isAlive()) {
                            t.interrupt();
                        }
                    } catch (Throwable thr) {
                        System.err.println(thr.getMessage());
                    }
                }
            }
            // Refresh the neighbor and reverse neighbor panels.
            nnPanel.revalidate();
            nnPanel.repaint();
            rnnPanel.revalidate();
            rnnPanel.repaint();
            // Visualize the neighbor occurrence profile of the selected image.
            DefaultPieDataset pieData = new DefaultPieDataset();
            for (int c = 0; c < numClasses; c++) {
                pieData.setValue(classNames[c], occurrenceProfilesAllK[neighborhoodSize - 1][index][c]);
            }
            JFreeChart chart = ChartFactory.createPieChart3D("occurrence " + "profile", pieData, true, true, false);
            PiePlot3D plot = (PiePlot3D) chart.getPlot();
            plot.setStartAngle(290);
            plot.setDirection(Rotation.CLOCKWISE);
            plot.setForegroundAlpha(0.5f);
            PieRenderer prend = new PieRenderer(classColors);
            prend.setColor(plot, pieData);
            ChartPanel chartPanel = new ChartPanel(chart);
            chartPanel.setPreferredSize(new Dimension(240, 200));
            occProfileChartHolder.removeAll();
            occProfileChartHolder.add(chartPanel);
            occProfileChartHolder.revalidate();
            occProfileChartHolder.repaint();
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }

    /**
     * This method sets the image of the specified history index as the
     * currently selected image and updates all the views.
     *
     * @param index Integer that is the history index of the image to select as
     * the current image.
     */
    private synchronized void setSelectedImageForHistoryIndex(int historyIndex) {
        // Update the selected image panels.
        BufferedImage photo = getPhoto(selectedImageHistory.get(historyIndex));
        selectedImagePanelClassNeighborMain.setImage(photo);
        selectedImagePanelClassNeighbor.setImage(photo);
        selectedImagePanelClass.setImage(photo);
        selectedImagePanelSearch.setImage(photo);
        int index = selectedImageHistory.get(historyIndex);
        // Update the labels with the new name.
        String shortPath = imgPaths.get(index).substring(workspace.getPath().length(),
                imgPaths.get(index).length());
        selectedImagePathLabelClassNeighborMain.setText(shortPath);
        selectedImagePathLabelClassNeighbor.setText(shortPath);
        selectedImagePathLabelClass.setText(shortPath);
        selectedImagePathLabelSearch.setText(shortPath);
        // Update the class colors.
        selectedImageLabelClassNeighborMain.setBackground(classColors[quantizedRepresentation.getLabelOf(index)]);
        selectedImageLabelClassNeighbor.setBackground(classColors[quantizedRepresentation.getLabelOf(index)]);
        selectedImageLabelClass.setBackground(classColors[quantizedRepresentation.getLabelOf(index)]);
        selectedImageLabelSearch.setBackground(classColors[quantizedRepresentation.getLabelOf(index)]);
        // Refresh the display.
        selectedImageLabelClassNeighborMain.setOpaque(true);
        selectedImageLabelClassNeighbor.setOpaque(true);
        selectedImageLabelClass.setOpaque(true);
        selectedImageLabelSearch.setOpaque(true);
        selectedImageLabelClassNeighborMain.repaint();
        selectedImageLabelClassNeighbor.repaint();
        selectedImageLabelClass.repaint();
        selectedImageLabelSearch.repaint();
        // Update the nearest neighbors and the reverse nearest neighbors.
        NeighborSetFinder nsf = getNSF();
        nnPanel.removeAll();
        rnnPanel.removeAll();
        nnPanel.revalidate();
        nnPanel.repaint();
        rnnPanel.revalidate();
        rnnPanel.repaint();
        int[][] kneighbors = nsf.getKNeighbors();
        for (int neighborIndex = 0; neighborIndex < neighborhoodSize; neighborIndex++) {
            BufferedImage thumb = thumbnails.get(kneighbors[index][neighborIndex]);
            try {
                Thread t = new Thread(new SetImageNeighborsHelper(nnPanel, thumb,
                        quantizedRepresentation.getLabelOf(kneighbors[index][neighborIndex]),
                        kneighbors[index][neighborIndex]));
                t.start();
                t.join(500);
                if (t.isAlive()) {
                    t.interrupt();
                }
            } catch (Throwable thr) {
                System.err.println(thr.getMessage());
            }
        }
        ArrayList<Integer>[] rrns = rnnSetsAllK[neighborhoodSize - 1];
        if (rrns[index] != null && rrns[index].size() > 0) {
            for (int i = 0; i < rrns[index].size(); i++) {
                BufferedImage thumb = thumbnails.get(rrns[index].get(i));
                try {
                    Thread t = new Thread(new SetImageNeighborsHelper(rnnPanel, thumb,
                            quantizedRepresentation.getLabelOf(rrns[index].get(i)), rrns[index].get(i)));
                    t.start();
                    t.join(500);
                    if (t.isAlive()) {
                        t.interrupt();
                    }
                } catch (Throwable thr) {
                    System.err.println(thr.getMessage());
                }
            }
        }
        // Refresh the neighbor and reverse neighbor panels.
        nnPanel.revalidate();
        nnPanel.repaint();
        rnnPanel.revalidate();
        rnnPanel.repaint();
        // Visualize the neighbor occurrence profile of the selected image.
        DefaultPieDataset pieData = new DefaultPieDataset();
        for (int c = 0; c < numClasses; c++) {
            pieData.setValue(classNames[c], occurrenceProfilesAllK[neighborhoodSize - 1][index][c]);
        }
        JFreeChart chart = ChartFactory.createPieChart3D("occurrence profile", pieData, true, true, false);
        PiePlot3D plot = (PiePlot3D) chart.getPlot();
        plot.setStartAngle(290);
        plot.setDirection(Rotation.CLOCKWISE);
        plot.setForegroundAlpha(0.5f);
        PieRenderer prend = new PieRenderer(classColors);
        prend.setColor(plot, pieData);
        ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setPreferredSize(new Dimension(240, 200));
        occProfileChartHolder.removeAll();
        occProfileChartHolder.add(chartPanel);
        occProfileChartHolder.revalidate();
        occProfileChartHolder.repaint();
    }

    /**
     * This method gets the photo for the provided image index.
     *
     * @param index Integer that is the image index in the data.
     * @return BufferedImage corresponding to the image index.
     */
    private BufferedImage getPhoto(int index) {
        if (images[index] == null) {
            int pathFeatureIndex = quantizedRepresentation.getIndexForAttributeName("relative_path");
            File inImageFile = new File(workspace,
                    "photos" + quantizedRepresentation.getInstance(index).sAttr[pathFeatureIndex]);
            try {
                images[index] = ImageIO.read(inImageFile);
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        }
        return images[index];
    }

    /**
     * This method returns the distances that are currently in use. If there is
     * a secondary distance matrix, it returns that one. If there is no
     * secondary distance matrix, it returns the primary matrix instead.
     *
     * @return float[][] that is the currently used distance matrix.
     */
    private float[][] getDistances() {
        if (distMatrixSecondary != null) {
            return distMatrixSecondary;
        } else {
            return distMatrixPrimary;
        }
    }

    /**
     * This method returns the currently employed CombinedMetric object for
     * distance calculations. If there are secondary distances in use, it
     * returns the secondary CombinedMetric object. If not, the primary one.
     *
     * @return CombinedMetric object that is currently in use.
     */
    private CombinedMetric getCombinedMetric() {
        if (secondaryCMet != null) {
            return secondaryCMet;
        } else {
            return primaryCMet;
        }
    }

    /**
     * This method returns the current NeighborSetFinder.
     *
     * @return NeighborSet finder that is currently in use by the system.
     */
    private NeighborSetFinder getNSF() {
        if (nsfSecondary != null) {
            return nsfSecondary;
        } else {
            return nsfPrimary;
        }
    }

    /**
     * Creates new form HubExplorer
     */
    public ImageHubExplorer() {
        initComponents();
        additionalInit();
    }

    /**
     * Initialization.
     */
    private void additionalInit() {
        // Initialize kNN and reverse neighbor panels.
        nnPanel.setLayout(new FlowLayout());
        rnnPanel.setLayout(new FlowLayout());
        nnPanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
        rnnPanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
        rnnPanel.setMaximumSize(new Dimension(60000, 400));
        // Initialize the neighborhood size selection slider.
        kSelectionSlider.setMajorTickSpacing(5);
        kSelectionSlider.setMinorTickSpacing(1);
        kSelectionSlider.setPaintLabels(true);
        kSelectionSlider.setPaintTicks(true);
        kSelectionSlider.addChangeListener(new SliderChanger());
        // Initialize various chart panels.
        classColorAndNamesPanel.setLayout(new VerticalFlowLayout());
        occProfileChartHolder.setLayout(new FlowLayout());
        classDistributionHolder.setLayout(new FlowLayout());
        chartHoldingPanelOccDistribution.setLayout(new FlowLayout());
        chartHoldingPanelOccDistribution.setPreferredSize(new Dimension(497, 191));
        queryNNPanel.setLayout(new VerticalFlowLayout());
        classifierPredictionsPanel.setLayout(new VerticalFlowLayout());
        // Initialize the scroll panes.
        prClassScrollPane.setPreferredSize(new Dimension(237, 432));
        prClassScrollPane.setMinimumSize(new Dimension(237, 432));
        prClassScrollPane.setMaximumSize(new Dimension(237, 432));
        queryNNScrollPane.setPreferredSize(new Dimension(207, 455));
        queryNNScrollPane.setMinimumSize(new Dimension(207, 455));
        queryNNScrollPane.setMaximumSize(new Dimension(207, 455));
        classesScrollPanel.setLayout(new VerticalFlowLayout());
        classesScrollPanel.setPreferredSize(new Dimension(760, 1168));
        classesScrollPanel.setMaximumSize(new Dimension(760, 1168));
        classesScrollPanel.setMinimumSize(new Dimension(760, 1168));
        classesScrollPane.setPreferredSize(new Dimension(760, 308));
        classesScrollPane.setMaximumSize(new Dimension(760, 308));
        classesScrollPane.setMinimumSize(new Dimension(760, 308));
        // Initialize the MDS component.
        mdsCollectionPanel.setPreferredSize(new Dimension(1500, 1500));
        mdsCollectionPanel.setMaximumSize(new Dimension(1500, 1500));
        mdsCollectionPanel.setMinimumSize(new Dimension(1500, 1500));
        // Initialize the kNN graph visualization component.
        neighborGraphScrollPane.setPreferredSize(new Dimension(550, 550));
        neighborGraphScrollPane.setMinimumSize(new Dimension(550, 550));
        neighborGraphScrollPane.setMaximumSize(new Dimension(550, 550));
        neighborGraphScrollPane.setVisible(true);
        selectedImagePathLabelClassNeighborMain.setPreferredSize(new Dimension(30, 16));
        selectedImagePathLabelClassNeighborMain.setMaximumSize(new Dimension(30, 16));
        selectedImagePathLabelClassNeighborMain.setMinimumSize(new Dimension(30, 16));
        jScrollPane1.setPreferredSize(new Dimension(30, 16));
        jScrollPane1.setMinimumSize(new Dimension(30, 16));
        jScrollPane1.setMaximumSize(new Dimension(30, 16));
    }

    /**
     * This listener handles the changes in the current neighborhood size by
     * moving the k-slider.
     */
    class SliderChanger implements ChangeListener {

        @Override
        public void stateChanged(ChangeEvent e) {
            if (!neighborStatsCalculated) {
                // If the kNN stats haven't been calculated, there is no need to
                // do anything.
                return;
            }
            Object src = e.getSource();
            if (src instanceof JSlider) {
                // Get the selected neighborhood size.
                neighborhoodSize = Math.max(((JSlider) src).getValue(), 1);
                // Get the index of the currently selected image.
                int index = -1;
                if (selectedImageHistory != null && selectedImageHistory.size() > 0) {
                    index = selectedImageHistory.get(selectedImageIndexInHistory);
                }
                // Get the object holding the kNN sets.
                NeighborSetFinder nsf = getNSF();
                // Reinitialize the panels.
                nnPanel.removeAll();
                rnnPanel.removeAll();
                nnPanel.revalidate();
                nnPanel.repaint();
                rnnPanel.revalidate();
                rnnPanel.repaint();
                if (index != -1) {
                    // Get the kNN sets.
                    int[][] kneighbors = nsf.getKNeighbors();
                    // Refresh the neighbor list.
                    for (int i = 0; i < neighborhoodSize; i++) {
                        ImagePanelWithClass neighbor = new ImagePanelWithClass(classColors);
                        neighbor.addMouseListener(new NeighborSelectionListener());
                        neighbor.setImage(thumbnails.get(kneighbors[index][i]),
                                quantizedRepresentation.getLabelOf(kneighbors[index][i]), kneighbors[index][i]);
                        nnPanel.add(neighbor);
                    }
                    // Refresh the reverse nearest neighbor list.
                    ArrayList<Integer>[] rrns = rnnSetsAllK[neighborhoodSize - 1];
                    if (rrns[index] != null && rrns[index].size() > 0) {
                        for (int i = 0; i < rrns[index].size(); i++) {
                            ImagePanelWithClass rrneighbor = new ImagePanelWithClass(classColors);
                            rrneighbor.setImage(thumbnails.get(rrns[index].get(i)),
                                    quantizedRepresentation.getLabelOf(rrns[index].get(i)), rrns[index].get(i));
                            rrneighbor.addMouseListener(new NeighborSelectionListener());
                            rnnPanel.add(rrneighbor);
                        }
                    }
                    // Refresh the occurrence profile of the current image.
                    DefaultPieDataset pieData = new DefaultPieDataset();
                    for (int c = 0; c < numClasses; c++) {
                        pieData.setValue(classNames[c], occurrenceProfilesAllK[neighborhoodSize - 1][index][c]);
                    }
                    JFreeChart chart = ChartFactory.createPieChart3D("occurrence profile", pieData, true, true,
                            false);
                    PiePlot3D plot = (PiePlot3D) chart.getPlot();
                    plot.setStartAngle(290);
                    plot.setDirection(Rotation.CLOCKWISE);
                    plot.setForegroundAlpha(0.5f);
                    PieRenderer prend = new PieRenderer(classColors);
                    prend.setColor(plot, pieData);
                    ChartPanel chartPanel = new ChartPanel(chart);
                    chartPanel.setPreferredSize(new Dimension(240, 200));
                    // Refresh the display.
                    occProfileChartHolder.removeAll();
                    occProfileChartHolder.add(chartPanel);
                    occProfileChartHolder.revalidate();
                    occProfileChartHolder.repaint();
                    nnPanel.revalidate();
                    nnPanel.repaint();
                    rnnPanel.revalidate();
                    rnnPanel.repaint();
                    // Refresh the kNN graph visualizations, as new edges might
                    // need to be inserted.
                    graphVServers[neighborhoodSize - 1].setPreferredSize(new Dimension(500, 500));
                    graphVServers[neighborhoodSize - 1].setMinimumSize(new Dimension(500, 500));
                    Layout<ImageNode, NeighborLink> layout = new CircleLayout(neighborGraphs[neighborhoodSize - 1]);
                    layout.setSize(new Dimension(500, 500));
                    layout.initialize();
                    VisualizationViewer<ImageNode, NeighborLink> vv = new VisualizationViewer<>(layout);
                    vv.setPreferredSize(new Dimension(550, 550));
                    vv.setMinimumSize(new Dimension(550, 550));
                    vv.setDoubleBuffered(true);
                    vv.setEnabled(true);
                    graphVServers[neighborhoodSize - 1] = vv;
                    vv.getRenderContext().setVertexIconTransformer(new IconTransformer<ImageNode, Icon>());
                    vv.getRenderContext().setVertexShapeTransformer(new ShapeTransformer<ImageNode, Shape>());
                    vv.getRenderContext().setEdgeArrowPredicate(new DirectionDisplayPredicate());
                    vv.getRenderContext().setEdgeLabelTransformer(new Transformer() {
                        @Override
                        public String transform(Object e) {
                            return (e.toString());
                        }
                    });
                    PluggableGraphMouse gm = new PluggableGraphMouse();
                    gm.add(new PickingGraphMousePlugin());
                    vv.setGraphMouse(gm);
                    vv.setBackground(Color.WHITE);
                    vv.setVisible(true);
                    final PickedState<ImageNode> pickedState = vv.getPickedVertexState();
                    pickedState.addItemListener(new ItemListener() {
                        @Override
                        public void itemStateChanged(ItemEvent e) {
                            Object subject = e.getItem();
                            if (subject instanceof ImageNode) {
                                ImageNode vertex = (ImageNode) subject;
                                if (pickedState.isPicked(vertex)) {
                                    setSelectedImageForIndex(vertex.id);
                                } else {
                                }
                            }
                        }
                    });
                    // Refresh the graph displays.
                    vv.validate();
                    neighborGraphScrollPane.setViewportView(graphVServers[neighborhoodSize - 1]);
                    neighborGraphScrollPane.revalidate();
                    neighborGraphScrollPane.repaint();
                }
                // Refresh the kNN stats in the main screen.
                setStatFieldsForK(neighborhoodSize);
                classesScrollPanel.removeAll();
                // Refresh the class summary panels
                for (int c = 0; c < numClasses; c++) {
                    ClassHubsPanel chp = new ClassHubsPanel(classColors[c], classNames[c]);
                    classStatsOverviews[c] = chp;
                    chp.setPointTypeDistribution(classPTypes[c]);
                    chp.revalidate();
                    chp.repaint();
                    // Lists of hubs, good hubs and bad hubs for the class.
                    JPanel hubsPanel = chp.getHubsPanel();
                    JPanel hubsPanelGood = chp.getGoodHubsPanel();
                    JPanel hubsPanelBad = chp.getBadHubsPanel();
                    hubsPanel.removeAll();
                    for (int i = 0; i < Math.min(50, classImageIndexes[c].size()); i++) {
                        BufferedImage thumb = thumbnails.get(classTopHubLists[neighborhoodSize - 1][c].get(i));
                        ImagePanelWithClass imgPan = new ImagePanelWithClass(classColors);
                        imgPan.addMouseListener(new NeighborSelectionListener());
                        imgPan.setImage(thumb,
                                quantizedRepresentation
                                        .getLabelOf(classTopHubLists[neighborhoodSize - 1][c].get(i)),
                                classTopHubLists[neighborhoodSize - 1][c].get(i));
                        hubsPanel.add(imgPan);
                    }
                    hubsPanel.revalidate();
                    hubsPanel.repaint();
                    hubsPanelGood.removeAll();
                    for (int i = 0; i < Math.min(50, classImageIndexes[c].size()); i++) {
                        BufferedImage thumb = thumbnails.get(classTopGoodHubsList[neighborhoodSize - 1][c].get(i));
                        ImagePanelWithClass imgPan = new ImagePanelWithClass(classColors);
                        imgPan.addMouseListener(new NeighborSelectionListener());
                        imgPan.setImage(thumb,
                                quantizedRepresentation
                                        .getLabelOf(classTopGoodHubsList[neighborhoodSize - 1][c].get(i)),
                                classTopGoodHubsList[neighborhoodSize - 1][c].get(i));
                        hubsPanelGood.add(imgPan);
                    }
                    hubsPanelGood.revalidate();
                    hubsPanelGood.repaint();
                    hubsPanelBad.removeAll();
                    for (int i = 0; i < Math.min(50, classImageIndexes[c].size()); i++) {
                        BufferedImage thumb = thumbnails.get(classTopBadHubsList[neighborhoodSize - 1][c].get(i));
                        ImagePanelWithClass imgPan = new ImagePanelWithClass(classColors);
                        imgPan.addMouseListener(new NeighborSelectionListener());
                        imgPan.setImage(thumb,
                                quantizedRepresentation
                                        .getLabelOf(classTopBadHubsList[neighborhoodSize - 1][c].get(i)),
                                classTopBadHubsList[neighborhoodSize - 1][c].get(i));
                        hubsPanelBad.add(imgPan);
                    }
                    hubsPanelBad.revalidate();
                    hubsPanelBad.repaint();
                    chp.revalidate();
                    chp.repaint();
                    classesScrollPanel.add(chp);
                }
                classesScrollPanel.revalidate();
                classesScrollPanel.repaint();
                classesScrollPane.revalidate();
                classesScrollPane.repaint();
                // Handle the data visualization in the MDS screen.
                if (imageCoordinatesXY != null) {
                    // In case some of the thumbnails crosses the bounding box
                    // of the MDS panel, offsets are set to compensate and to
                    // ensure all images are visible in their entirety.
                    float offX, offY;
                    if (highestHubnesses != null) {
                        float maxOccurrenceFrequency = ArrayUtil.max(highestHubnesses[neighborhoodSize - 1]);
                        float[] thumbSizes = new float[highestHubnesses[neighborhoodSize - 1].length];
                        ArrayList<Rectangle2D> bounds = new ArrayList<>(thumbSizes.length);
                        ArrayList<ImagePanelWithClass> imgsMDS = new ArrayList<>(thumbSizes.length);
                        for (int i = 0; i < thumbSizes.length; i++) {
                            // Calculate the thumbnail size.
                            try {
                                thumbSizes[i] = pointScale(highestHubnesses[neighborhoodSize - 1][i],
                                        maxOccurrenceFrequency, minImageScale, maxImageScale);
                            } catch (Exception eSecond) {
                                System.err.println(eSecond.getMessage());
                            }
                            if (imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][0]
                                    + thumbSizes[i] / 2 > mdsCollectionPanel.getWidth()) {
                                offX = (thumbSizes[i] / 2 - (mdsCollectionPanel.getWidth()
                                        - imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][0]));
                            } else if (imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][0]
                                    - thumbSizes[i] / 2 < 0) {
                                offX = imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][0];
                            } else {
                                offX = thumbSizes[i] / 2;
                            }
                            if (imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][1]
                                    + thumbSizes[i] / 2 > mdsCollectionPanel.getHeight()) {
                                offY = (thumbSizes[i] / 2 - (mdsCollectionPanel.getHeight()
                                        - imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][1]));
                            } else if (imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][1]
                                    - thumbSizes[i] / 2 < 0) {
                                offY = imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][1];
                            } else {
                                offY = thumbSizes[i] / 2;
                            }
                            // Get the image thumbnail to show.
                            BufferedImage thumb = thumbnails.get(highestHubIndexes[neighborhoodSize - 1][i]);
                            ImagePanelWithClass imgPan = new ImagePanelWithClass(classColors);
                            imgPan.addMouseListener(new NeighborSelectionListener());
                            imgPan.setImage(thumb,
                                    quantizedRepresentation.getLabelOf(highestHubIndexes[neighborhoodSize - 1][i]),
                                    highestHubIndexes[neighborhoodSize - 1][i]);
                            imgsMDS.add(imgPan);
                            // Set the bounding rectangle.
                            bounds.add(new Rectangle2D.Float(
                                    imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][0] - offX,
                                    imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][1] - offY,
                                    thumbSizes[i], thumbSizes[i]));
                        }
                        // Set the images for display in the MDS overview panel.
                        mdsCollectionPanel.setImageSet(imgsMDS, bounds);
                        setMDSBackground();
                        // Refresh the display.
                        mdsCollectionPanel.revalidate();
                        mdsCollectionPanel.repaint();
                    }
                }
            }
        }
    }

    /**
     * This method loads the distance matrix from a file.
     *
     * @param dMatFile File that holds the distance matrix.
     * @return float[][] that is the distance matrix.
     * @throws Exception
     */
    public float[][] loadDMatFromFile(File dMatFile) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(dMatFile)));
        float[][] dMatLoaded = null;
        String line;
        String[] lineItems;
        try {
            int size = Integer.parseInt(br.readLine());
            dMatLoaded = new float[size][];
            for (int i = 0; i < size - 1; i++) {
                dMatLoaded[i] = new float[size - i - 1];
                line = br.readLine();
                lineItems = line.split(",");
                for (int j = 0; j < lineItems.length; j++) {
                    dMatLoaded[i][j] = Float.parseFloat(lineItems[j]);
                }
            }
            dMatLoaded[size - 1] = new float[0];
        } catch (IOException | NumberFormatException e) {
            throw e;
        } finally {
            br.close();
        }
        return dMatLoaded;
    }

    /**
     * This method prints a distance matrix to a file.
     *
     * @param distMat float[][] that is the distance matrix.
     * @param dMatFile File to write the matrix to.
     * @throws Exception
     */
    public void printDMatToFile(float[][] distMat, File dMatFile) throws Exception {
        FileUtil.createFile(dMatFile);
        try (PrintWriter pw = new PrintWriter(new FileWriter(dMatFile));) {
            pw.println(distMat.length);
            for (int i = 0; i < distMat.length - 1; i++) {
                pw.print(distMat[i][0]);
                for (int j = 1; j < distMat[i].length; j++) {
                    pw.print("," + distMat[i][j]);
                }
                pw.println();
            }
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 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() {

        hubTab = new javax.swing.JTabbedPane();
        dataMainPanel = new javax.swing.JPanel();
        selectedImagePanelClassNeighborMain = new gui.images.ImagePanel();
        selectedImageLabelClassNeighborMain = new javax.swing.JLabel();
        mdsScrollPane = new javax.swing.JScrollPane();
        mdsCollectionPanel = new gui.images.ImagesDisplayPanel();
        workspaceLabelTxt = new javax.swing.JLabel();
        collectionSizeLabelTxt = new javax.swing.JLabel();
        workspaceLabelValue = new javax.swing.JLabel();
        collectionSizeLabelValue = new javax.swing.JLabel();
        kSelectionSlider = new javax.swing.JSlider();
        nhSizeLabelTxt = new javax.swing.JLabel();
        numClassesLabelTxt = new javax.swing.JLabel();
        numClassesLabelValue = new javax.swing.JLabel();
        hRelatedPropTxt = new javax.swing.JLabel();
        skewnwessLabelTxt = new javax.swing.JLabel();
        skewnessLabelValue = new javax.swing.JLabel();
        kurtosisLabelTxt = new javax.swing.JLabel();
        kurtosisLabelValue = new javax.swing.JLabel();
        nkEntropyLabelTxt = new javax.swing.JLabel();
        nkEntropyLabelValue = new javax.swing.JLabel();
        jLabel1 = new javax.swing.JLabel();
        rnkEntropyValue = new javax.swing.JLabel();
        nkEntropySkewnessTxt = new javax.swing.JLabel();
        rnkEntropySkewnessTxt = new javax.swing.JLabel();
        nkEntropySkewnessValues = new javax.swing.JLabel();
        rnkEntropySkewnessValue = new javax.swing.JLabel();
        percAboveLabelTxt = new javax.swing.JLabel();
        percAboveLabelValue = new javax.swing.JLabel();
        hubsLabelTxt = new javax.swing.JLabel();
        orphansLabelTxt = new javax.swing.JLabel();
        regularLabelTxt = new javax.swing.JLabel();
        majorDegLabelTxt = new javax.swing.JLabel();
        hubsLabelValue = new javax.swing.JLabel();
        orphansLabelValue = new javax.swing.JLabel();
        regularLabelValue = new javax.swing.JLabel();
        majorDegLabelValue = new javax.swing.JLabel();
        badHubnessLabelTxt = new javax.swing.JLabel();
        badHubnessLabelValue = new javax.swing.JLabel();
        chartHoldingPanelOccDistribution = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        selectedImagePathLabelClassNeighborMain = new javax.swing.JLabel();
        neighborPanel = new javax.swing.JPanel();
        selectedImagePanelClassNeighbor = new gui.images.ImagePanel();
        selectedImageLabelClassNeighbor = new javax.swing.JLabel();
        nnScrollPane = new javax.swing.JScrollPane();
        nnPanel = new javax.swing.JPanel();
        rnnScrollPane = new javax.swing.JScrollPane();
        rnnPanel = new javax.swing.JPanel();
        nnScrollLabelTxt = new javax.swing.JLabel();
        rnnScrollLabelTxt = new javax.swing.JLabel();
        occProfileChartHolder = new javax.swing.JPanel();
        noccProfLabelTxt = new javax.swing.JLabel();
        neighborGraphScrollPane = new javax.swing.JScrollPane();
        addSelectedButton = new javax.swing.JButton();
        addNNsButton = new javax.swing.JButton();
        addRNNsButton = new javax.swing.JButton();
        jScrollPane2 = new javax.swing.JScrollPane();
        selectedImagePathLabelClassNeighbor = new javax.swing.JLabel();
        removeVertexButton = new javax.swing.JButton();
        removeAllButton = new javax.swing.JButton();
        classPanel = new javax.swing.JPanel();
        selectedImagePanelClass = new gui.images.ImagePanel();
        selectedImageLabelClass = new javax.swing.JLabel();
        confusionMatScrollPane = new javax.swing.JScrollPane();
        classHubnessTable = new javax.swing.JTable();
        classesScrollPane = new javax.swing.JScrollPane();
        classesScrollPanel = new javax.swing.JPanel();
        classDistributionHolder = new javax.swing.JPanel();
        jScrollPane3 = new javax.swing.JScrollPane();
        selectedImagePathLabelClass = new javax.swing.JLabel();
        cNamesScrollPane = new javax.swing.JScrollPane();
        jScrollPane5 = new javax.swing.JScrollPane();
        classColorAndNamesPanel = new javax.swing.JPanel();
        searchPanel = new javax.swing.JPanel();
        selectedImagePanelSearch = new gui.images.ImagePanel();
        selectedImageLabelSearch = new javax.swing.JLabel();
        searchQLabelTxt = new javax.swing.JLabel();
        queryImagePanel = new gui.images.ImagePanel();
        imageBrowseButton = new javax.swing.JButton();
        jTextField1 = new javax.swing.JTextField();
        queryQTextLabelTxt = new javax.swing.JLabel();
        queryNNScrollPane = new javax.swing.JScrollPane();
        queryNNPanel = new javax.swing.JPanel();
        simResLabelTxt = new javax.swing.JLabel();
        searchButton = new javax.swing.JButton();
        prClassLabelTxt = new javax.swing.JLabel();
        prClassScrollPane = new javax.swing.JScrollPane();
        classifierPredictionsPanel = new javax.swing.JPanel();
        collectionSearchButton = new javax.swing.JButton();
        jScrollPane4 = new javax.swing.JScrollPane();
        selectedImagePathLabelSearch = new javax.swing.JLabel();
        reRankingButton = new javax.swing.JButton();
        menuBar = new javax.swing.JMenuBar();
        collectionMenu = new javax.swing.JMenu();
        workspaceMenuItem = new javax.swing.JMenuItem();
        importItem = new javax.swing.JMenuItem();
        dMatrixMenu = new javax.swing.JMenu();
        distImportItem = new javax.swing.JMenuItem();
        distCalculateMenu = new javax.swing.JMenu();
        manhattanDistItem = new javax.swing.JMenuItem();
        distCalcEuclideanItem = new javax.swing.JMenuItem();
        distCalcCosineItem = new javax.swing.JMenuItem();
        tanimotoMenuItem = new javax.swing.JMenuItem();
        klMenuItem = new javax.swing.JMenuItem();
        bcMenuItem = new javax.swing.JMenuItem();
        canMenuItem = new javax.swing.JMenuItem();
        neighborStatsItem = new javax.swing.JMenuItem();
        mdsVisualizeItem = new javax.swing.JMenuItem();
        selImgPathMenuItem = new javax.swing.JMenuItem();
        majorHubSelectionItem = new javax.swing.JMenuItem();
        metricLearningMenu = new javax.swing.JMenu();
        secondaryMetricMenu = new javax.swing.JMenu();
        simcosMenuItem = new javax.swing.JMenuItem();
        simhubMenuItem = new javax.swing.JMenuItem();
        mpMenuItem = new javax.swing.JMenuItem();
        localScalingItem = new javax.swing.JMenuItem();
        nicdmItem = new javax.swing.JMenuItem();
        loadSecondaryDistancesItem = new javax.swing.JMenuItem();
        editMenu = new javax.swing.JMenu();
        previousMenuItem = new javax.swing.JMenuItem();
        nextMenuItem = new javax.swing.JMenuItem();
        screenCaptureMenu = new javax.swing.JMenu();
        mdsScreenCaptureItem = new javax.swing.JMenuItem();
        graphScreenCaptureItem = new javax.swing.JMenuItem();
        codebookMenu = new javax.swing.JMenu();
        loadCodebookItem = new javax.swing.JMenuItem();
        loadCodebookProfileMenuItem = new javax.swing.JMenuItem();
        classificationMenu = new javax.swing.JMenu();
        trainModelsItem = new javax.swing.JMenuItem();
        selImageMenu = new javax.swing.JMenu();
        selSIFTmenuItem = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Image Hub Explorer");

        javax.swing.GroupLayout selectedImagePanelClassNeighborMainLayout = new javax.swing.GroupLayout(
                selectedImagePanelClassNeighborMain);
        selectedImagePanelClassNeighborMain.setLayout(selectedImagePanelClassNeighborMainLayout);
        selectedImagePanelClassNeighborMainLayout.setHorizontalGroup(selectedImagePanelClassNeighborMainLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 241, Short.MAX_VALUE));
        selectedImagePanelClassNeighborMainLayout.setVerticalGroup(selectedImagePanelClassNeighborMainLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 257, Short.MAX_VALUE));

        selectedImageLabelClassNeighborMain.setText("Current Image");

        mdsScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        mdsScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        mdsCollectionPanel.setName(""); // NOI18N
        mdsCollectionPanel.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                mdsCollectionPanelMouseClicked(evt);
            }
        });

        javax.swing.GroupLayout mdsCollectionPanelLayout = new javax.swing.GroupLayout(mdsCollectionPanel);
        mdsCollectionPanel.setLayout(mdsCollectionPanelLayout);
        mdsCollectionPanelLayout.setHorizontalGroup(mdsCollectionPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 2000, Short.MAX_VALUE));
        mdsCollectionPanelLayout.setVerticalGroup(mdsCollectionPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 2000, Short.MAX_VALUE));

        mdsScrollPane.setViewportView(mdsCollectionPanel);

        workspaceLabelTxt.setText("Workspace:");

        collectionSizeLabelTxt.setText("Collection size:");

        workspaceLabelValue.setText("...");

        collectionSizeLabelValue.setText("...");

        kSelectionSlider.setMaximum(50);
        kSelectionSlider.setPaintLabels(true);
        kSelectionSlider.setPaintTicks(true);
        kSelectionSlider.setToolTipText("Select the neighborhood size, k");
        kSelectionSlider.setValue(1);

        nhSizeLabelTxt.setText("Neighborhood size (k):");

        numClassesLabelTxt.setText("Num. Classes:");

        numClassesLabelValue.setText("...");

        hRelatedPropTxt.setBackground(new java.awt.Color(102, 153, 255));
        hRelatedPropTxt.setText("Hubness-related properties:");

        skewnwessLabelTxt.setText("Occ. Skewness: ");

        skewnessLabelValue.setText("...");

        kurtosisLabelTxt.setText("Occ. Kurtosis:");

        kurtosisLabelValue.setText("...");

        nkEntropyLabelTxt.setText("Nk Entropy:");

        nkEntropyLabelValue.setText("...");

        jLabel1.setText("RNk Entropy:");

        rnkEntropyValue.setText("...");

        nkEntropySkewnessTxt.setText("Nk Ent. Skew:");

        rnkEntropySkewnessTxt.setText("RNk Ent Skew:");

        nkEntropySkewnessValues.setText("...");

        rnkEntropySkewnessValue.setText("...");

        percAboveLabelTxt.setText("Perc Nk(x) > 0");

        percAboveLabelValue.setText("...");

        hubsLabelTxt.setText("Hubs:");

        orphansLabelTxt.setText("Orphans:");

        regularLabelTxt.setText("Regular:");

        majorDegLabelTxt.setText("Major Deg:");

        hubsLabelValue.setText("...");

        orphansLabelValue.setText("...");

        regularLabelValue.setText("...");

        majorDegLabelValue.setText("...");

        badHubnessLabelTxt.setText("Mislabel perc:");

        badHubnessLabelValue.setText("...");

        chartHoldingPanelOccDistribution.setMaximumSize(new java.awt.Dimension(497, 191));
        chartHoldingPanelOccDistribution.setMinimumSize(new java.awt.Dimension(497, 191));

        javax.swing.GroupLayout chartHoldingPanelOccDistributionLayout = new javax.swing.GroupLayout(
                chartHoldingPanelOccDistribution);
        chartHoldingPanelOccDistribution.setLayout(chartHoldingPanelOccDistributionLayout);
        chartHoldingPanelOccDistributionLayout.setHorizontalGroup(chartHoldingPanelOccDistributionLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 497, Short.MAX_VALUE));
        chartHoldingPanelOccDistributionLayout.setVerticalGroup(chartHoldingPanelOccDistributionLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 221, Short.MAX_VALUE));

        jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        selectedImagePathLabelClassNeighborMain.setText("Path:");
        jScrollPane1.setViewportView(selectedImagePathLabelClassNeighborMain);

        javax.swing.GroupLayout dataMainPanelLayout = new javax.swing.GroupLayout(dataMainPanel);
        dataMainPanel.setLayout(dataMainPanelLayout);
        dataMainPanelLayout.setHorizontalGroup(dataMainPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, dataMainPanelLayout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(mdsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 536,
                                javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(18, 18, 18)
                        .addGroup(dataMainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addGroup(dataMainPanelLayout.createSequentialGroup().addGroup(dataMainPanelLayout
                                        .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                        .addComponent(rnkEntropySkewnessTxt).addComponent(nkEntropySkewnessTxt)
                                        .addComponent(jLabel1).addComponent(nkEntropyLabelTxt)
                                        .addComponent(hRelatedPropTxt, javax.swing.GroupLayout.DEFAULT_SIZE,
                                                javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                        .addGroup(dataMainPanelLayout.createSequentialGroup()
                                                .addComponent(workspaceLabelTxt)
                                                .addPreferredGap(
                                                        javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                                .addComponent(workspaceLabelValue,
                                                        javax.swing.GroupLayout.PREFERRED_SIZE, 169,
                                                        javax.swing.GroupLayout.PREFERRED_SIZE))
                                        .addGroup(dataMainPanelLayout.createSequentialGroup()
                                                .addGroup(dataMainPanelLayout
                                                        .createParallelGroup(
                                                                javax.swing.GroupLayout.Alignment.TRAILING, false)
                                                        .addComponent(numClassesLabelTxt,
                                                                javax.swing.GroupLayout.Alignment.LEADING,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(collectionSizeLabelTxt,
                                                                javax.swing.GroupLayout.Alignment.LEADING,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE))
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addGroup(dataMainPanelLayout
                                                        .createParallelGroup(
                                                                javax.swing.GroupLayout.Alignment.LEADING, false)
                                                        .addComponent(numClassesLabelValue,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(collectionSizeLabelValue,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE, 68,
                                                                Short.MAX_VALUE)))
                                        .addComponent(kSelectionSlider, javax.swing.GroupLayout.Alignment.TRAILING,
                                                javax.swing.GroupLayout.PREFERRED_SIZE, 249,
                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addGroup(dataMainPanelLayout.createSequentialGroup()
                                                .addGroup(dataMainPanelLayout
                                                        .createParallelGroup(
                                                                javax.swing.GroupLayout.Alignment.TRAILING, false)
                                                        .addComponent(kurtosisLabelTxt,
                                                                javax.swing.GroupLayout.Alignment.LEADING,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(skewnwessLabelTxt,
                                                                javax.swing.GroupLayout.Alignment.LEADING,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE))
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addGroup(dataMainPanelLayout
                                                        .createParallelGroup(
                                                                javax.swing.GroupLayout.Alignment.LEADING)
                                                        .addComponent(nkEntropySkewnessValues)
                                                        .addGroup(dataMainPanelLayout.createSequentialGroup()
                                                                .addGroup(dataMainPanelLayout.createParallelGroup(
                                                                        javax.swing.GroupLayout.Alignment.LEADING)
                                                                        .addComponent(skewnessLabelValue)
                                                                        .addComponent(kurtosisLabelValue)
                                                                        .addComponent(nkEntropyLabelValue)
                                                                        .addComponent(rnkEntropyValue)
                                                                        .addComponent(rnkEntropySkewnessValue))
                                                                .addGap(28, 28, 28)
                                                                .addGroup(dataMainPanelLayout.createParallelGroup(
                                                                        javax.swing.GroupLayout.Alignment.LEADING)
                                                                        .addGroup(dataMainPanelLayout
                                                                                .createSequentialGroup()
                                                                                .addComponent(badHubnessLabelTxt)
                                                                                .addPreferredGap(
                                                                                        javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                                                .addComponent(badHubnessLabelValue))
                                                                        .addGroup(dataMainPanelLayout
                                                                                .createSequentialGroup()
                                                                                .addComponent(majorDegLabelTxt)
                                                                                .addPreferredGap(
                                                                                        javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                                                .addComponent(majorDegLabelValue))
                                                                        .addGroup(dataMainPanelLayout
                                                                                .createSequentialGroup()
                                                                                .addComponent(regularLabelTxt)
                                                                                .addGap(18, 18, 18)
                                                                                .addComponent(regularLabelValue))
                                                                        .addGroup(dataMainPanelLayout
                                                                                .createSequentialGroup()
                                                                                .addGroup(dataMainPanelLayout
                                                                                        .createParallelGroup(
                                                                                                javax.swing.GroupLayout.Alignment.LEADING)
                                                                                        .addComponent(hubsLabelTxt)
                                                                                        .addComponent(
                                                                                                orphansLabelTxt))
                                                                                .addGap(18, 18, 18)
                                                                                .addGroup(dataMainPanelLayout
                                                                                        .createParallelGroup(
                                                                                                javax.swing.GroupLayout.Alignment.LEADING)
                                                                                        .addComponent(
                                                                                                orphansLabelValue)
                                                                                        .addComponent(
                                                                                                hubsLabelValue)))))))
                                        .addGroup(dataMainPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING,
                                                        false)
                                                .addGroup(dataMainPanelLayout.createSequentialGroup()
                                                        .addComponent(percAboveLabelTxt).addGap(18, 18, 18)
                                                        .addComponent(percAboveLabelValue,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE))
                                                .addComponent(nhSizeLabelTxt,
                                                        javax.swing.GroupLayout.Alignment.LEADING)))
                                        .addGap(7, 7, 7)
                                        .addGroup(dataMainPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                                .addComponent(selectedImageLabelClassNeighborMain,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                                .addComponent(selectedImagePanelClassNeighborMain,
                                                        javax.swing.GroupLayout.Alignment.TRAILING,
                                                        javax.swing.GroupLayout.PREFERRED_SIZE,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        javax.swing.GroupLayout.PREFERRED_SIZE)
                                                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE,
                                                        241, javax.swing.GroupLayout.PREFERRED_SIZE)))
                                .addComponent(chartHoldingPanelOccDistribution,
                                        javax.swing.GroupLayout.PREFERRED_SIZE,
                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                        javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addContainerGap()));
        dataMainPanelLayout.setVerticalGroup(dataMainPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(dataMainPanelLayout.createSequentialGroup().addContainerGap().addGroup(dataMainPanelLayout
                        .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addComponent(mdsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 593,
                                javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGroup(dataMainPanelLayout.createSequentialGroup().addGroup(dataMainPanelLayout
                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addGroup(dataMainPanelLayout.createSequentialGroup()
                                        .addGroup(dataMainPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                .addComponent(workspaceLabelTxt).addComponent(workspaceLabelValue))
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addGroup(dataMainPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
                                                        false)
                                                .addComponent(collectionSizeLabelTxt,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                                .addComponent(collectionSizeLabelValue,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                                        .addGap(19, 19, 19)
                                        .addGroup(dataMainPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
                                                        false)
                                                .addComponent(numClassesLabelTxt,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                                .addComponent(numClassesLabelValue,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                                        .addGap(17, 17, 17).addComponent(hRelatedPropTxt).addGap(15, 15, 15)
                                        .addGroup(dataMainPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                .addComponent(skewnwessLabelTxt).addComponent(skewnessLabelValue)
                                                .addComponent(hubsLabelTxt).addComponent(hubsLabelValue))
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addGroup(dataMainPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                .addComponent(kurtosisLabelTxt).addComponent(kurtosisLabelValue)
                                                .addComponent(orphansLabelTxt).addComponent(orphansLabelValue))
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addGroup(dataMainPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                .addComponent(nkEntropyLabelTxt).addComponent(nkEntropyLabelValue)
                                                .addComponent(regularLabelTxt).addComponent(regularLabelValue))
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addGroup(dataMainPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                .addComponent(jLabel1).addComponent(rnkEntropyValue)
                                                .addComponent(majorDegLabelTxt).addComponent(majorDegLabelValue))
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addGroup(dataMainPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                .addComponent(nkEntropySkewnessTxt)
                                                .addComponent(nkEntropySkewnessValues))
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addGroup(dataMainPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                .addComponent(rnkEntropySkewnessTxt)
                                                .addComponent(rnkEntropySkewnessValue)
                                                .addComponent(badHubnessLabelTxt)
                                                .addComponent(badHubnessLabelValue))
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addGroup(dataMainPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                .addComponent(percAboveLabelTxt).addComponent(percAboveLabelValue)))
                                .addComponent(selectedImagePanelClassNeighborMain,
                                        javax.swing.GroupLayout.PREFERRED_SIZE,
                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                        javax.swing.GroupLayout.PREFERRED_SIZE))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addGroup(dataMainPanelLayout
                                        .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                        .addComponent(selectedImageLabelClassNeighborMain,
                                                javax.swing.GroupLayout.DEFAULT_SIZE, 53, Short.MAX_VALUE)
                                        .addComponent(nhSizeLabelTxt, javax.swing.GroupLayout.DEFAULT_SIZE,
                                                javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addGroup(dataMainPanelLayout
                                        .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                        .addComponent(kSelectionSlider, javax.swing.GroupLayout.PREFERRED_SIZE,
                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 53,
                                                javax.swing.GroupLayout.PREFERRED_SIZE))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(
                                        chartHoldingPanelOccDistribution, javax.swing.GroupLayout.PREFERRED_SIZE,
                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                        javax.swing.GroupLayout.PREFERRED_SIZE)))
                        .addContainerGap()));

        hubTab.addTab("Data Overview", dataMainPanel);

        javax.swing.GroupLayout selectedImagePanelClassNeighborLayout = new javax.swing.GroupLayout(
                selectedImagePanelClassNeighbor);
        selectedImagePanelClassNeighbor.setLayout(selectedImagePanelClassNeighborLayout);
        selectedImagePanelClassNeighborLayout.setHorizontalGroup(selectedImagePanelClassNeighborLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 222, Short.MAX_VALUE));
        selectedImagePanelClassNeighborLayout.setVerticalGroup(selectedImagePanelClassNeighborLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 224, Short.MAX_VALUE));

        selectedImageLabelClassNeighbor.setText("Current Image");

        nnScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        nnScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        javax.swing.GroupLayout nnPanelLayout = new javax.swing.GroupLayout(nnPanel);
        nnPanel.setLayout(nnPanelLayout);
        nnPanelLayout.setHorizontalGroup(nnPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 353, Short.MAX_VALUE));
        nnPanelLayout.setVerticalGroup(nnPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGap(0, 100, Short.MAX_VALUE));

        nnScrollPane.setViewportView(nnPanel);

        rnnScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        rnnScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        javax.swing.GroupLayout rnnPanelLayout = new javax.swing.GroupLayout(rnnPanel);
        rnnPanel.setLayout(rnnPanelLayout);
        rnnPanelLayout.setHorizontalGroup(rnnPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 373, Short.MAX_VALUE));
        rnnPanelLayout.setVerticalGroup(rnnPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 100, Short.MAX_VALUE));

        rnnScrollPane.setViewportView(rnnPanel);

        nnScrollLabelTxt.setFont(new java.awt.Font("Tahoma", 0, 18)); // NOI18N
        nnScrollLabelTxt.setText("NNs:");

        rnnScrollLabelTxt.setFont(new java.awt.Font("Tahoma", 0, 18)); // NOI18N
        rnnScrollLabelTxt.setText("RNNs:");

        javax.swing.GroupLayout occProfileChartHolderLayout = new javax.swing.GroupLayout(occProfileChartHolder);
        occProfileChartHolder.setLayout(occProfileChartHolderLayout);
        occProfileChartHolderLayout.setHorizontalGroup(occProfileChartHolderLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 236, Short.MAX_VALUE));
        occProfileChartHolderLayout.setVerticalGroup(occProfileChartHolderLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 185, Short.MAX_VALUE));

        noccProfLabelTxt.setText("Selected image neighbor occurrence profile");

        neighborGraphScrollPane
                .setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        neighborGraphScrollPane
                .setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        addSelectedButton.setText("Add selected");
        addSelectedButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addSelectedButtonActionPerformed(evt);
            }
        });

        addNNsButton.setText("Add NNs");
        addNNsButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addNNsButtonActionPerformed(evt);
            }
        });

        addRNNsButton.setText("Add RNNs");
        addRNNsButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addRNNsButtonActionPerformed(evt);
            }
        });

        selectedImagePathLabelClassNeighbor.setText("Path:");
        jScrollPane2.setViewportView(selectedImagePathLabelClassNeighbor);

        removeVertexButton.setText("Remove Sel.");
        removeVertexButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                removeVertexButtonActionPerformed(evt);
            }
        });

        removeAllButton.setText("Remove All");
        removeAllButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                removeAllButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout neighborPanelLayout = new javax.swing.GroupLayout(neighborPanel);
        neighborPanel.setLayout(neighborPanelLayout);
        neighborPanelLayout.setHorizontalGroup(neighborPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(neighborPanelLayout.createSequentialGroup().addContainerGap()
                        .addComponent(neighborGraphScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 529,
                                javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(18, 18, 18)
                        .addGroup(neighborPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addGroup(neighborPanelLayout.createSequentialGroup().addGap(31, 31, 31)
                                        .addGroup(neighborPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                                .addComponent(nnScrollLabelTxt,
                                                        javax.swing.GroupLayout.PREFERRED_SIZE, 46,
                                                        javax.swing.GroupLayout.PREFERRED_SIZE)
                                                .addComponent(rnnScrollLabelTxt,
                                                        javax.swing.GroupLayout.PREFERRED_SIZE, 68,
                                                        javax.swing.GroupLayout.PREFERRED_SIZE))
                                        .addGap(44, 44, 44)
                                        .addGroup(neighborPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
                                                        false)
                                                .addComponent(nnScrollPane, 0, 0, Short.MAX_VALUE)
                                                .addComponent(rnnScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        361, Short.MAX_VALUE)))
                                .addGroup(javax.swing.GroupLayout.Alignment.TRAILING,
                                        neighborPanelLayout.createSequentialGroup().addGroup(neighborPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                                                .addGroup(javax.swing.GroupLayout.Alignment.LEADING,
                                                        neighborPanelLayout.createSequentialGroup()
                                                                .addGroup(neighborPanelLayout.createParallelGroup(
                                                                        javax.swing.GroupLayout.Alignment.TRAILING)
                                                                        .addComponent(occProfileChartHolder,
                                                                                javax.swing.GroupLayout.Alignment.LEADING,
                                                                                javax.swing.GroupLayout.PREFERRED_SIZE,
                                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                                                        .addComponent(noccProfLabelTxt,
                                                                                javax.swing.GroupLayout.Alignment.LEADING,
                                                                                javax.swing.GroupLayout.PREFERRED_SIZE,
                                                                                276,
                                                                                javax.swing.GroupLayout.PREFERRED_SIZE))
                                                                .addPreferredGap(
                                                                        javax.swing.LayoutStyle.ComponentPlacement.RELATED,
                                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                        Short.MAX_VALUE))
                                                .addGroup(neighborPanelLayout.createSequentialGroup()
                                                        .addGroup(neighborPanelLayout
                                                                .createParallelGroup(
                                                                        javax.swing.GroupLayout.Alignment.TRAILING)
                                                                .addGroup(neighborPanelLayout
                                                                        .createSequentialGroup()
                                                                        .addComponent(addRNNsButton)
                                                                        .addPreferredGap(
                                                                                javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                                        .addComponent(addNNsButton))
                                                                .addComponent(removeAllButton))
                                                        .addPreferredGap(
                                                                javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                        .addGroup(neighborPanelLayout
                                                                .createParallelGroup(
                                                                        javax.swing.GroupLayout.Alignment.TRAILING,
                                                                        false)
                                                                .addComponent(removeVertexButton,
                                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                        Short.MAX_VALUE)
                                                                .addComponent(addSelectedButton,
                                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                        Short.MAX_VALUE))
                                                        .addGap(18, 18, 18)))
                                                .addGroup(neighborPanelLayout
                                                        .createParallelGroup(
                                                                javax.swing.GroupLayout.Alignment.LEADING)
                                                        .addComponent(selectedImageLabelClassNeighbor,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(selectedImagePanelClassNeighbor,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                                        .addComponent(jScrollPane2,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE))))
                        .addContainerGap()));
        neighborPanelLayout.setVerticalGroup(neighborPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(neighborPanelLayout.createSequentialGroup().addContainerGap().addGroup(neighborPanelLayout
                        .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addComponent(neighborGraphScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 593,
                                Short.MAX_VALUE)
                        .addGroup(neighborPanelLayout.createSequentialGroup().addGroup(neighborPanelLayout
                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addGroup(neighborPanelLayout.createSequentialGroup()
                                        .addComponent(occProfileChartHolder, javax.swing.GroupLayout.PREFERRED_SIZE,
                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                        .addComponent(noccProfLabelTxt, javax.swing.GroupLayout.DEFAULT_SIZE, 36,
                                                Short.MAX_VALUE))
                                .addComponent(selectedImagePanelClassNeighbor,
                                        javax.swing.GroupLayout.PREFERRED_SIZE,
                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                        javax.swing.GroupLayout.PREFERRED_SIZE))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addGroup(neighborPanelLayout
                                        .createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                                        .addGroup(neighborPanelLayout.createSequentialGroup()
                                                .addGroup(neighborPanelLayout
                                                        .createParallelGroup(
                                                                javax.swing.GroupLayout.Alignment.TRAILING)
                                                        .addGroup(javax.swing.GroupLayout.Alignment.LEADING,
                                                                neighborPanelLayout.createSequentialGroup()
                                                                        .addComponent(
                                                                                selectedImageLabelClassNeighbor)
                                                                        .addPreferredGap(
                                                                                javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                                        .addComponent(jScrollPane2,
                                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                                68, Short.MAX_VALUE))
                                                        .addGroup(neighborPanelLayout.createSequentialGroup()
                                                                .addGroup(neighborPanelLayout.createParallelGroup(
                                                                        javax.swing.GroupLayout.Alignment.BASELINE)
                                                                        .addComponent(removeVertexButton)
                                                                        .addComponent(removeAllButton))
                                                                .addPreferredGap(
                                                                        javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                                .addGroup(neighborPanelLayout.createParallelGroup(
                                                                        javax.swing.GroupLayout.Alignment.BASELINE)
                                                                        .addComponent(addSelectedButton,
                                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                                47, Short.MAX_VALUE)
                                                                        .addComponent(addNNsButton,
                                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                                47, Short.MAX_VALUE))))
                                                .addGap(16, 16, 16))
                                        .addGroup(neighborPanelLayout.createSequentialGroup()
                                                .addComponent(addRNNsButton, javax.swing.GroupLayout.PREFERRED_SIZE,
                                                        45, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                .addGap(18, 18, 18)))
                                .addGroup(neighborPanelLayout
                                        .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                        .addComponent(nnScrollLabelTxt, javax.swing.GroupLayout.DEFAULT_SIZE, 125,
                                                Short.MAX_VALUE)
                                        .addComponent(nnScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 115,
                                                javax.swing.GroupLayout.PREFERRED_SIZE))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                .addGroup(neighborPanelLayout
                                        .createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                                        .addComponent(rnnScrollLabelTxt, javax.swing.GroupLayout.DEFAULT_SIZE, 122,
                                                Short.MAX_VALUE)
                                        .addComponent(rnnScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 111,
                                                javax.swing.GroupLayout.PREFERRED_SIZE))))
                        .addContainerGap()));

        hubTab.addTab("Neighbor View", neighborPanel);

        javax.swing.GroupLayout selectedImagePanelClassLayout = new javax.swing.GroupLayout(
                selectedImagePanelClass);
        selectedImagePanelClass.setLayout(selectedImagePanelClassLayout);
        selectedImagePanelClassLayout.setHorizontalGroup(selectedImagePanelClassLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 222, Short.MAX_VALUE));
        selectedImagePanelClassLayout.setVerticalGroup(selectedImagePanelClassLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 224, Short.MAX_VALUE));

        selectedImageLabelClass.setText("Current Image");

        confusionMatScrollPane
                .setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        confusionMatScrollPane
                .setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        classHubnessTable
                .setModel(new javax.swing.table.DefaultTableModel(
                        new Object[][] { { null, null, null, null }, { null, null, null, null },
                                { null, null, null, null }, { null, null, null, null } },
                        new String[] { "Title 1", "Title 2", "Title 3", "Title 4" }));
        classHubnessTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
        classHubnessTable.setMaximumSize(new java.awt.Dimension(2000, 2000));
        classHubnessTable.setMinimumSize(new java.awt.Dimension(120, 120));
        classHubnessTable.setRowHeight(30);
        confusionMatScrollPane.setViewportView(classHubnessTable);

        classesScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        classesScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        classesScrollPanel.setPreferredSize(new java.awt.Dimension(760, 1508));

        javax.swing.GroupLayout classesScrollPanelLayout = new javax.swing.GroupLayout(classesScrollPanel);
        classesScrollPanel.setLayout(classesScrollPanelLayout);
        classesScrollPanelLayout.setHorizontalGroup(classesScrollPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 760, Short.MAX_VALUE));
        classesScrollPanelLayout.setVerticalGroup(classesScrollPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 1508, Short.MAX_VALUE));

        classesScrollPane.setViewportView(classesScrollPanel);

        javax.swing.GroupLayout classDistributionHolderLayout = new javax.swing.GroupLayout(
                classDistributionHolder);
        classDistributionHolder.setLayout(classDistributionHolderLayout);
        classDistributionHolderLayout.setHorizontalGroup(classDistributionHolderLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 428, Short.MAX_VALUE));
        classDistributionHolderLayout.setVerticalGroup(classDistributionHolderLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 0, Short.MAX_VALUE));

        selectedImagePathLabelClass.setText("Path:");
        jScrollPane3.setViewportView(selectedImagePathLabelClass);

        cNamesScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        cNamesScrollPane.setMaximumSize(new java.awt.Dimension(100, 100));
        cNamesScrollPane.setMinimumSize(new java.awt.Dimension(100, 100));

        javax.swing.GroupLayout classColorAndNamesPanelLayout = new javax.swing.GroupLayout(
                classColorAndNamesPanel);
        classColorAndNamesPanel.setLayout(classColorAndNamesPanelLayout);
        classColorAndNamesPanelLayout.setHorizontalGroup(classColorAndNamesPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 350, Short.MAX_VALUE));
        classColorAndNamesPanelLayout.setVerticalGroup(classColorAndNamesPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 255, Short.MAX_VALUE));

        jScrollPane5.setViewportView(classColorAndNamesPanel);

        cNamesScrollPane.setViewportView(jScrollPane5);

        javax.swing.GroupLayout classPanelLayout = new javax.swing.GroupLayout(classPanel);
        classPanel.setLayout(classPanelLayout);
        classPanelLayout.setHorizontalGroup(classPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(classPanelLayout.createSequentialGroup().addContainerGap().addGroup(classPanelLayout
                        .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addComponent(classesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 764, Short.MAX_VALUE)
                        .addGroup(classPanelLayout.createSequentialGroup()
                                .addComponent(classDistributionHolder, javax.swing.GroupLayout.PREFERRED_SIZE,
                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                        javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addGap(18, 18, 18).addComponent(cNamesScrollPane,
                                        javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(classPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addGroup(classPanelLayout.createSequentialGroup().addGroup(classPanelLayout
                                        .createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                                        .addGroup(classPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING,
                                                        false)
                                                .addComponent(selectedImageLabelClass,
                                                        javax.swing.GroupLayout.Alignment.LEADING,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                                .addComponent(selectedImagePanelClass,
                                                        javax.swing.GroupLayout.Alignment.LEADING,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                                        .addComponent(confusionMatScrollPane,
                                                javax.swing.GroupLayout.PREFERRED_SIZE, 262,
                                                javax.swing.GroupLayout.PREFERRED_SIZE))
                                        .addGap(21, 21, 21))
                                .addGroup(classPanelLayout
                                        .createSequentialGroup().addComponent(jScrollPane3,
                                                javax.swing.GroupLayout.PREFERRED_SIZE, 278, Short.MAX_VALUE)
                                        .addContainerGap()))));
        classPanelLayout.setVerticalGroup(classPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(classPanelLayout.createSequentialGroup().addContainerGap()
                        .addComponent(selectedImagePanelClass, javax.swing.GroupLayout.PREFERRED_SIZE,
                                javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(selectedImageLabelClass, javax.swing.GroupLayout.PREFERRED_SIZE, 27,
                                javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 86, Short.MAX_VALUE)
                        .addGap(18, 18, 18).addComponent(confusionMatScrollPane,
                                javax.swing.GroupLayout.PREFERRED_SIZE, 237,
                                javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, classPanelLayout.createSequentialGroup()
                        .addGroup(classPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addComponent(classDistributionHolder, javax.swing.GroupLayout.DEFAULT_SIZE,
                                        javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addGroup(classPanelLayout.createSequentialGroup()
                                        .addComponent(cNamesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 244,
                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addGap(0, 32, Short.MAX_VALUE)))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(classesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 331,
                                javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(11, 11, 11)));

        hubTab.addTab("Class View", classPanel);

        javax.swing.GroupLayout selectedImagePanelSearchLayout = new javax.swing.GroupLayout(
                selectedImagePanelSearch);
        selectedImagePanelSearch.setLayout(selectedImagePanelSearchLayout);
        selectedImagePanelSearchLayout.setHorizontalGroup(selectedImagePanelSearchLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 222, Short.MAX_VALUE));
        selectedImagePanelSearchLayout.setVerticalGroup(selectedImagePanelSearchLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 224, Short.MAX_VALUE));

        selectedImageLabelSearch.setText("Current Image");

        searchQLabelTxt.setText("Do you want to search the image collection?");

        javax.swing.GroupLayout queryImagePanelLayout = new javax.swing.GroupLayout(queryImagePanel);
        queryImagePanel.setLayout(queryImagePanelLayout);
        queryImagePanelLayout.setHorizontalGroup(queryImagePanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 237, Short.MAX_VALUE));
        queryImagePanelLayout.setVerticalGroup(queryImagePanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 237, Short.MAX_VALUE));

        imageBrowseButton.setText("Browse");
        imageBrowseButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                imageBrowseButtonActionPerformed(evt);
            }
        });

        jTextField1.setText("                       -- Enter text --");
        jTextField1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jTextField1ActionPerformed(evt);
            }
        });

        queryQTextLabelTxt.setText("You can also search with a textual query:");

        queryNNScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        queryNNScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        javax.swing.GroupLayout queryNNPanelLayout = new javax.swing.GroupLayout(queryNNPanel);
        queryNNPanel.setLayout(queryNNPanelLayout);
        queryNNPanelLayout.setHorizontalGroup(queryNNPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 184, Short.MAX_VALUE));
        queryNNPanelLayout.setVerticalGroup(queryNNPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 432, Short.MAX_VALUE));

        queryNNScrollPane.setViewportView(queryNNPanel);

        simResLabelTxt.setText("Here is a list of most similar results:");

        searchButton.setText("SEARCH");
        searchButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                searchButtonActionPerformed(evt);
            }
        });

        prClassLabelTxt.setText("Predicted class for k = 10:");

        prClassScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        prClassScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        javax.swing.GroupLayout classifierPredictionsPanelLayout = new javax.swing.GroupLayout(
                classifierPredictionsPanel);
        classifierPredictionsPanel.setLayout(classifierPredictionsPanelLayout);
        classifierPredictionsPanelLayout.setHorizontalGroup(classifierPredictionsPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 241, Short.MAX_VALUE));
        classifierPredictionsPanelLayout.setVerticalGroup(classifierPredictionsPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 432, Short.MAX_VALUE));

        prClassScrollPane.setViewportView(classifierPredictionsPanel);

        collectionSearchButton.setText("Select from collection");
        collectionSearchButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                collectionSearchButtonActionPerformed(evt);
            }
        });

        selectedImagePathLabelSearch.setText("Path:");
        jScrollPane4.setViewportView(selectedImagePathLabelSearch);

        reRankingButton.setText("Re-rank");
        reRankingButton.setToolTipText("Perform Secondary\nHubness-aware Re-ranking");
        reRankingButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                reRankingButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout searchPanelLayout = new javax.swing.GroupLayout(searchPanel);
        searchPanel.setLayout(searchPanelLayout);
        searchPanelLayout.setHorizontalGroup(searchPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(searchPanelLayout.createSequentialGroup().addContainerGap().addGroup(searchPanelLayout
                        .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addComponent(searchQLabelTxt, javax.swing.GroupLayout.PREFERRED_SIZE, 319,
                                javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGroup(searchPanelLayout
                                .createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                                .addComponent(queryQTextLabelTxt, javax.swing.GroupLayout.Alignment.LEADING,
                                        javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE,
                                        Short.MAX_VALUE)
                                .addComponent(jTextField1, javax.swing.GroupLayout.Alignment.LEADING)
                                .addComponent(searchButton, javax.swing.GroupLayout.Alignment.LEADING,
                                        javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE,
                                        Short.MAX_VALUE))
                        .addGroup(searchPanelLayout
                                .createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                                .addGroup(javax.swing.GroupLayout.Alignment.LEADING,
                                        searchPanelLayout.createSequentialGroup().addComponent(imageBrowseButton)
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addComponent(collectionSearchButton,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                                .addComponent(queryImagePanel, javax.swing.GroupLayout.Alignment.LEADING,
                                        javax.swing.GroupLayout.PREFERRED_SIZE,
                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                        javax.swing.GroupLayout.PREFERRED_SIZE)))
                        .addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addGroup(searchPanelLayout.createSequentialGroup().addGap(18, 18, 18)
                                        .addGroup(searchPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
                                                        false)
                                                .addComponent(simResLabelTxt, javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                                .addComponent(queryNNScrollPane))
                                        .addGroup(searchPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                                .addGroup(searchPanelLayout.createSequentialGroup()
                                                        .addGap(44, 44, 44).addComponent(prClassLabelTxt,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE, 226,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE))
                                                .addGroup(searchPanelLayout.createSequentialGroup()
                                                        .addGap(29, 29, 29).addComponent(prClassScrollPane,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE, 260,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE))))
                                .addGroup(searchPanelLayout.createSequentialGroup().addGap(59, 59, 59).addComponent(
                                        reRankingButton, javax.swing.GroupLayout.PREFERRED_SIZE, 119,
                                        javax.swing.GroupLayout.PREFERRED_SIZE)))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addComponent(jScrollPane4, javax.swing.GroupLayout.PREFERRED_SIZE, 236,
                                        Short.MAX_VALUE)
                                .addGroup(searchPanelLayout
                                        .createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                                        .addComponent(selectedImageLabelSearch,
                                                javax.swing.GroupLayout.Alignment.LEADING,
                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                        .addComponent(selectedImagePanelSearch,
                                                javax.swing.GroupLayout.Alignment.LEADING,
                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
                        .addContainerGap()));
        searchPanelLayout.setVerticalGroup(searchPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(searchPanelLayout.createSequentialGroup().addContainerGap().addGroup(searchPanelLayout
                        .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addGroup(searchPanelLayout.createSequentialGroup().addGroup(searchPanelLayout
                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addGroup(searchPanelLayout.createSequentialGroup().addGroup(searchPanelLayout
                                        .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                        .addComponent(searchQLabelTxt, javax.swing.GroupLayout.PREFERRED_SIZE, 30,
                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addComponent(simResLabelTxt, javax.swing.GroupLayout.PREFERRED_SIZE, 51,
                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addComponent(prClassLabelTxt))
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addGroup(searchPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                .addComponent(imageBrowseButton)
                                                .addComponent(collectionSearchButton).addComponent(reRankingButton))
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                        .addComponent(queryImagePanel, javax.swing.GroupLayout.PREFERRED_SIZE,
                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                javax.swing.GroupLayout.PREFERRED_SIZE))
                                .addGroup(searchPanelLayout.createSequentialGroup()
                                        .addComponent(selectedImagePanelSearch,
                                                javax.swing.GroupLayout.PREFERRED_SIZE,
                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(selectedImageLabelSearch,
                                                javax.swing.GroupLayout.PREFERRED_SIZE, 27,
                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(jScrollPane4, javax.swing.GroupLayout.PREFERRED_SIZE, 69,
                                                javax.swing.GroupLayout.PREFERRED_SIZE)))
                                .addGap(13, 13, 13).addComponent(queryQTextLabelTxt).addGap(18, 18, 18)
                                .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 103,
                                        javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED).addComponent(
                                        searchButton, javax.swing.GroupLayout.DEFAULT_SIZE, 111, Short.MAX_VALUE))
                        .addGroup(javax.swing.GroupLayout.Alignment.TRAILING,
                                searchPanelLayout
                                        .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                                        .addComponent(prClassScrollPane).addComponent(queryNNScrollPane)))
                        .addContainerGap()));

        hubTab.addTab("Search", searchPanel);

        collectionMenu.setLabel("Collection");

        workspaceMenuItem.setText("Select workspace");
        workspaceMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                workspaceMenuItemActionPerformed(evt);
            }
        });
        collectionMenu.add(workspaceMenuItem);

        importItem.setLabel("Import data");
        importItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                importItemActionPerformed(evt);
            }
        });
        collectionMenu.add(importItem);

        dMatrixMenu.setText("Distances and Neighbor Sets");

        distImportItem.setText("Import");
        distImportItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                distImportItemActionPerformed(evt);
            }
        });
        dMatrixMenu.add(distImportItem);

        distCalculateMenu.setText("Calculate");

        manhattanDistItem.setText("Manhattan");
        manhattanDistItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                manhattanDistItemActionPerformed(evt);
            }
        });
        distCalculateMenu.add(manhattanDistItem);

        distCalcEuclideanItem.setText("Euclidean");
        distCalcEuclideanItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                distCalcEuclideanItemActionPerformed(evt);
            }
        });
        distCalculateMenu.add(distCalcEuclideanItem);

        distCalcCosineItem.setText("Cosine");
        distCalcCosineItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                distCalcCosineItemActionPerformed(evt);
            }
        });
        distCalculateMenu.add(distCalcCosineItem);

        tanimotoMenuItem.setText("Tanimoto");
        tanimotoMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                tanimotoMenuItemActionPerformed(evt);
            }
        });
        distCalculateMenu.add(tanimotoMenuItem);

        klMenuItem.setText("KL divergence");
        klMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                klMenuItemActionPerformed(evt);
            }
        });
        distCalculateMenu.add(klMenuItem);

        bcMenuItem.setText("Bray-Curtis");
        bcMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                bcMenuItemActionPerformed(evt);
            }
        });
        distCalculateMenu.add(bcMenuItem);

        canMenuItem.setText("Canberra");
        canMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                canMenuItemActionPerformed(evt);
            }
        });
        distCalculateMenu.add(canMenuItem);

        dMatrixMenu.add(distCalculateMenu);

        collectionMenu.add(dMatrixMenu);

        neighborStatsItem.setText("Calculate Neighbor Stats");
        neighborStatsItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                neighborStatsItemActionPerformed(evt);
            }
        });
        collectionMenu.add(neighborStatsItem);

        mdsVisualizeItem.setText("MDS Visualize");
        mdsVisualizeItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mdsVisualizeItemActionPerformed(evt);
            }
        });
        collectionMenu.add(mdsVisualizeItem);

        selImgPathMenuItem.setText("Select image by browsing");
        selImgPathMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                selImgPathMenuItemActionPerformed(evt);
            }
        });
        collectionMenu.add(selImgPathMenuItem);

        majorHubSelectionItem.setText("Select major hub");
        majorHubSelectionItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                majorHubSelectionItemActionPerformed(evt);
            }
        });
        collectionMenu.add(majorHubSelectionItem);

        menuBar.add(collectionMenu);

        metricLearningMenu.setText("Metric Learning");

        secondaryMetricMenu.setText("Calculate secondary metric");

        simcosMenuItem.setText("simcos shared neighbor sim");
        simcosMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                simcosMenuItemActionPerformed(evt);
            }
        });
        secondaryMetricMenu.add(simcosMenuItem);

        simhubMenuItem.setText("simhub shared neighbor sim");
        simhubMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                simhubMenuItemActionPerformed(evt);
            }
        });
        secondaryMetricMenu.add(simhubMenuItem);

        mpMenuItem.setText("mutual proximity");
        mpMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mpMenuItemActionPerformed(evt);
            }
        });
        secondaryMetricMenu.add(mpMenuItem);

        localScalingItem.setText("local scaling");
        localScalingItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                localScalingItemActionPerformed(evt);
            }
        });
        secondaryMetricMenu.add(localScalingItem);

        nicdmItem.setText("NICDM");
        nicdmItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                nicdmItemActionPerformed(evt);
            }
        });
        secondaryMetricMenu.add(nicdmItem);

        metricLearningMenu.add(secondaryMetricMenu);

        loadSecondaryDistancesItem.setText("Load secondary distance matrix");
        loadSecondaryDistancesItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                loadSecondaryDistancesItemActionPerformed(evt);
            }
        });
        metricLearningMenu.add(loadSecondaryDistancesItem);

        menuBar.add(metricLearningMenu);

        editMenu.setText("Edit");

        previousMenuItem.setText("Previous");
        previousMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                previousMenuItemActionPerformed(evt);
            }
        });
        editMenu.add(previousMenuItem);

        nextMenuItem.setText("Next");
        nextMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                nextMenuItemActionPerformed(evt);
            }
        });
        editMenu.add(nextMenuItem);

        screenCaptureMenu.setText("Screen capture");

        mdsScreenCaptureItem.setText("MDS screen");
        mdsScreenCaptureItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mdsScreenCaptureItemActionPerformed(evt);
            }
        });
        screenCaptureMenu.add(mdsScreenCaptureItem);

        graphScreenCaptureItem.setText("Graph screen");
        graphScreenCaptureItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                graphScreenCaptureItemActionPerformed(evt);
            }
        });
        screenCaptureMenu.add(graphScreenCaptureItem);

        editMenu.add(screenCaptureMenu);

        menuBar.add(editMenu);

        codebookMenu.setText("Codebook");

        loadCodebookItem.setText("load Codebook");
        loadCodebookItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                loadCodebookItemActionPerformed(evt);
            }
        });
        codebookMenu.add(loadCodebookItem);

        loadCodebookProfileMenuItem.setText("load Codebook Profile");
        loadCodebookProfileMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                loadCodebookProfileMenuItemActionPerformed(evt);
            }
        });
        codebookMenu.add(loadCodebookProfileMenuItem);

        menuBar.add(codebookMenu);

        classificationMenu.setText("Classification");

        trainModelsItem.setText("Train models");
        trainModelsItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                trainModelsItemActionPerformed(evt);
            }
        });
        classificationMenu.add(trainModelsItem);

        menuBar.add(classificationMenu);

        selImageMenu.setText("Selected Image");

        selSIFTmenuItem.setText("Visual words assessment view");
        selSIFTmenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                selSIFTmenuItemActionPerformed(evt);
            }
        });
        selImageMenu.add(selSIFTmenuItem);

        menuBar.add(selImageMenu);

        setJMenuBar(menuBar);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup().addContainerGap()
                        .addComponent(hubTab, javax.swing.GroupLayout.PREFERRED_SIZE, 1073, Short.MAX_VALUE)
                        .addContainerGap()));
        layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup().addComponent(hubTab).addContainerGap()));

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

    private void mdsCollectionPanelMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mdsCollectionPanelMouseClicked
    }//GEN-LAST:event_mdsCollectionPanelMouseClicked

    private void jTextField1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jTextField1ActionPerformed
    }//GEN-LAST:event_jTextField1ActionPerformed

    /**
     * This method sets the workspace to the selected directory.
     *
     * @param evt ActionEvent object.
     */
    private void workspaceMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_workspaceMenuItemActionPerformed
        JFileChooser jfc = new JFileChooser(currentDirectory);
        jfc.setDialogTitle("Set Workspace");
        jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        int rVal = jfc.showOpenDialog(ImageHubExplorer.this);
        if (rVal == JFileChooser.APPROVE_OPTION) {
            currentDirectory = jfc.getSelectedFile();
            workspace = jfc.getSelectedFile();
            workspaceLabelValue.setText(workspace.getAbsolutePath());
        }
    }//GEN-LAST:event_workspaceMenuItemActionPerformed

    /**
     * This class is used for calculating the primary distance matrix.
     */
    private class DistanceCalcHelperPrimary implements Runnable {

        boolean calcNSets = true;
        File dir;

        /**
         * Initialization.
         *
         * @param calcNSets Boolean flag indicating whether to also calculate
         * the kNN sets at once.
         * @param dir Directory where to persist the matrix and the kNN sets.
         */
        public DistanceCalcHelperPrimary(boolean calcNSets, File dir) {
            this.calcNSets = calcNSets;
            this.dir = dir;
        }

        @Override
        public void run() {
            try {
                distMatrixPrimary = quantizedRepresentation.calculateDistMatrixMultThr(primaryCMet, 8);
                JOptionPane.showMessageDialog(frameReference, "Distances properly calculated.");
                if (calcNSets) {
                    Thread t = new Thread(new NSetCalcHelperPrimary());
                    t.start();
                    try {
                        t.join();
                    } catch (Throwable thr) {
                        System.err.println(thr.getMessage());
                    }
                    nsfPrimary.setDistances(distMatrixPrimary);
                }
                printDMatToFile(distMatrixPrimary, new File(dir, "dMat.txt"));
                nsfPrimary.saveNeighborSets(new File(dir, "knnSets.txt"));
                imageCoordinatesXY = null;
            } catch (Exception e) {
                JOptionPane.showMessageDialog(frameReference, "An error occurred: " + e.getMessage(),
                        "Error message", JOptionPane.ERROR_MESSAGE);
            } finally {
                busyCalculating = false;
            }
        }
    }

    /**
     * This class is used for calculating the kNN sets in the primary metric.
     */
    private class NSetCalcHelperPrimary implements Runnable {

        /**
         * The default constructor.
         */
        public NSetCalcHelperPrimary() {
        }

        @Override
        public void run() {
            try {
                nsfPrimary = new NeighborSetFinder(quantizedRepresentation, distMatrixPrimary, primaryCMet);
                nsfPrimary.calculateNeighborSetsMultiThr(50, 8);
            } catch (Exception e) {
            } finally {
                busyCalculating = false;
            }
        }
    }

    /**
     * This method calculates the Manhattan distances from the representation.
     *
     * @param evt ActionEvent object.
     */
    private void manhattanDistItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_manhattanDistItemActionPerformed
        if (!busyCalculating) {
            busyCalculating = true;
            try {
                primaryCMet = CombinedMetric.FLOAT_MANHATTAN;
                // Designate a file to save the matrix to.
                primaryDMatFile = new File(workspace, "distancesNNSets" + File.separator
                        + primaryCMet.getFloatMetric().getClass().getName() + File.separator + "dMat.txt");
                boolean calcNSets = !(new File(primaryDMatFile.getParentFile(), "knnSets.txt")).exists();
                if (quantizedRepresentation != null && !quantizedRepresentation.isEmpty()
                        && !primaryDMatFile.exists()) {
                    Thread t = new Thread(
                            new DistanceCalcHelperPrimary(calcNSets, primaryDMatFile.getParentFile()));
                    t.start();
                } else if (primaryDMatFile.exists()) {
                    // If these distances already exist, just load them.
                    loadDistancesAndNeighbors(primaryDMatFile, PRIMARY_METRIC);
                }
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }//GEN-LAST:event_manhattanDistItemActionPerformed

    /**
     * This method calculates the Euclidean distances from the representation.
     *
     * @param evt ActionEvent object.
     */
    private void distCalcEuclideanItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_distCalcEuclideanItemActionPerformed
        if (!busyCalculating) {
            busyCalculating = true;
            try {
                primaryCMet = CombinedMetric.FLOAT_EUCLIDEAN;
                // Designate a file to save the matrix to.
                primaryDMatFile = new File(workspace, "distancesNNSets" + File.separator
                        + primaryCMet.getFloatMetric().getClass().getName() + File.separator + "dMat.txt");
                boolean calcNSets = !(new File(primaryDMatFile.getParentFile(), "knnSets.txt")).exists();
                if (quantizedRepresentation != null && !quantizedRepresentation.isEmpty()
                        && !primaryDMatFile.exists()) {
                    Thread t = new Thread(
                            new DistanceCalcHelperPrimary(calcNSets, primaryDMatFile.getParentFile()));
                    t.start();
                } else if (primaryDMatFile.exists()) {
                    // If these distances already exist, just load them.
                    loadDistancesAndNeighbors(primaryDMatFile, PRIMARY_METRIC);
                }
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }//GEN-LAST:event_distCalcEuclideanItemActionPerformed

    /**
     * This method calculates the cosine distances from the representation.
     *
     * @param evt ActionEvent object.
     */
    private void distCalcCosineItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_distCalcCosineItemActionPerformed
        if (!busyCalculating) {
            busyCalculating = true;
            try {
                primaryCMet = CombinedMetric.FLOAT_COSINE;
                // Designate a file to save the matrix to.
                primaryDMatFile = new File(workspace, "distancesNNSets" + File.separator
                        + primaryCMet.getFloatMetric().getClass().getName() + File.separator + "dMat.txt");
                boolean calcNSets = !(new File(primaryDMatFile.getParentFile(), "knnSets.txt")).exists();
                if (quantizedRepresentation != null && !quantizedRepresentation.isEmpty()
                        && !primaryDMatFile.exists()) {
                    Thread t = new Thread(
                            new DistanceCalcHelperPrimary(calcNSets, primaryDMatFile.getParentFile()));
                    t.start();
                } else if (primaryDMatFile.exists()) {
                    // If these distances already exist, just load them.
                    loadDistancesAndNeighbors(primaryDMatFile, PRIMARY_METRIC);
                }
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }//GEN-LAST:event_distCalcCosineItemActionPerformed

    /**
     * This method imports the distances and neighbor sets from the specified
     * files.
     *
     * @param evt ActionEvent object.
     */
    private void distImportItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_distImportItemActionPerformed
        JFileChooser jfc = new JFileChooser(currentDirectory);
        jfc.setDialogTitle("Choose distances file. " + "Neighbor sets will be automatically loaded if available");
        jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
        int rVal = jfc.showOpenDialog(ImageHubExplorer.this);
        if (rVal == JFileChooser.APPROVE_OPTION) {
            currentDirectory = jfc.getSelectedFile().getParentFile();
            primaryDMatFile = jfc.getSelectedFile();
            busyCalculating = true;
            try {
                loadDistancesAndNeighbors(primaryDMatFile, PRIMARY_METRIC);
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }//GEN-LAST:event_distImportItemActionPerformed

    /**
     * This method performs the data representation import.
     *
     * @param evt ActionEvent object.
     */
    private void importItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importItemActionPerformed
        Thread t = new Thread(new ImportHelper());
        t.start();
    }//GEN-LAST:event_importItemActionPerformed

    /**
     * This method loads the codebook definition from the specified file.
     *
     * @param evt ActionEvent object.
     */
    private void loadCodebookItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadCodebookItemActionPerformed
        JFileChooser jfc = new JFileChooser(currentDirectory);
        jfc.setDialogTitle("Load SIFT codebook");
        jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
        int rVal = jfc.showOpenDialog(ImageHubExplorer.this);
        if (rVal == JFileChooser.APPROVE_OPTION) {
            currentDirectory = jfc.getSelectedFile().getParentFile();
            File cbFile = jfc.getSelectedFile();
            busyCalculating = true;
            try {
                codebook = new GenericCodeBook();
                codebook.loadCodeBookFromFile(cbFile);
                JOptionPane.showMessageDialog(frameReference, "Load completed");
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }//GEN-LAST:event_loadCodebookItemActionPerformed

    /**
     * This method selects and image as the current image by browsing through
     * the image dataset directly and selecting the corresponding image file.
     *
     * @param evt ActionEvent object.
     */
    private void selImgPathMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selImgPathMenuItemActionPerformed
        JFileChooser jfc = new JFileChooser(currentDirectory);
        jfc.setDialogTitle("Select an image");
        jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
        int rVal = jfc.showOpenDialog(ImageHubExplorer.this);
        if (rVal == JFileChooser.APPROVE_OPTION) {
            currentDirectory = jfc.getSelectedFile().getParentFile();
            File imageFile = jfc.getSelectedFile();
            busyCalculating = true;
            try {
                // Look up the image index in the dataset by retrieving it from
                // the image path map.
                int index = pathIndexMap.get(imageFile.getPath());
                setSelectedImageForIndex(index);
                JOptionPane.showMessageDialog(frameReference, "Selection performed");
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }//GEN-LAST:event_selImgPathMenuItemActionPerformed

    /**
     * This method shifts the focus back to the previously examined image.
     *
     * @param evt ActionEvent object.
     */
    private void previousMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_previousMenuItemActionPerformed
        if (selectedImageIndexInHistory > 0) {
            selectedImageIndexInHistory--;
        }
        setSelectedImageForHistoryIndex(selectedImageIndexInHistory);
    }//GEN-LAST:event_previousMenuItemActionPerformed

    /**
     * This method shifts the focus forward to the next image in the history.
     *
     * @param evt ActionEvent object.
     */
    private void nextMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextMenuItemActionPerformed
        if (selectedImageIndexInHistory < selectedImageHistory.size() - 1) {
            selectedImageIndexInHistory++;
        }
        setSelectedImageForHistoryIndex(selectedImageIndexInHistory);
    }//GEN-LAST:event_nextMenuItemActionPerformed

    /**
     * This method loads the secondary distances and neighbor sets from the
     * specified files, if available.
     *
     * @param evt ActionEvent object.
     */
    private void loadSecondaryDistancesItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadSecondaryDistancesItemActionPerformed
        JFileChooser jfc = new JFileChooser(currentDirectory);
        jfc.setDialogTitle("Choose distances file. Neighbor sets will be " + "automatically loaded if available");
        jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
        int rVal = jfc.showOpenDialog(ImageHubExplorer.this);
        if (rVal == JFileChooser.APPROVE_OPTION) {
            currentDirectory = jfc.getSelectedFile().getParentFile();
            secondaryDMatFile = jfc.getSelectedFile();
            busyCalculating = true;
            try {
                loadDistancesAndNeighbors(secondaryDMatFile, SECONDARY_METRIC);
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }//GEN-LAST:event_loadSecondaryDistancesItemActionPerformed

    /**
     * Calculate the secondary simcos distances.
     *
     * @param evt ActionEvent object.
     */
    private void simcosMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_simcosMenuItemActionPerformed
        Thread t = new Thread(new SimCosHelper());
        t.start();
    }//GEN-LAST:event_simcosMenuItemActionPerformed

    private class SimCosHelper implements Runnable {

        /**
         * The default constructor.
         */
        public SimCosHelper() {
        }

        @Override
        public void run() {
            if (busyCalculating) {
                // If the system is currently performing calculations, abort the
                // call.
                return;
            }
            busyCalculating = true;
            try {
                // The primary and secondary matrix files.
                File pDir = primaryDMatFile.getParentFile();
                File sDir = new File(pDir, "secondary" + File.separator + "simcos");
                File matFile = new File(sDir, "dMat.txt");
                if (matFile.exists() && secondaryLoadFlag) {
                    // Load if it exists.
                    distMatrixSecondary = loadDMatFromFile(matFile);
                } else {
                    // Calculate the distances if it doesn't.
                    SharedNeighborFinder snf = new SharedNeighborFinder(nsfPrimary);
                    snf.setNumClasses(numClasses);
                    snf.setPrimaryMetricsCalculator(primaryCMet);
                    snf.countSharedNeighborsMultiThread(8);
                    // What we first get is the similarities, so we'll need to
                    // subtract them from the max to get the distances.
                    distMatrixSecondary = snf.getSharedNeighborCounts();
                    for (int i = 0; i < distMatrixSecondary.length; i++) {
                        for (int j = 0; j < distMatrixSecondary[i].length; j++) {
                            distMatrixSecondary[i][j] = 50 - distMatrixSecondary[i][j];
                        }
                    }
                    // Set the secondary CombinedMetric object.
                    secondaryCMet = new SharedNeighborCalculator(snf, SharedNeighborCalculator.WeightingType.NONE);
                }
                // Load or calculate the secondary kNN sets.
                File neighborsFile = new File(sDir, "knnSets.txt");
                if (neighborsFile.exists() && secondaryLoadFlag) {
                    nsfSecondary = NeighborSetFinder.loadNSF(neighborsFile, quantizedRepresentation);
                    nsfSecondary.setDistances(distMatrixSecondary);
                } else {
                    nsfSecondary = new NeighborSetFinder(quantizedRepresentation, distMatrixSecondary,
                            secondaryCMet);
                    nsfSecondary.calculateNeighborSetsMultiThr(50, 8);
                }
                printDMatToFile(distMatrixSecondary, matFile);
                nsfSecondary.saveNeighborSets(neighborsFile);
                neighborStatsCalculated = false;
                imageCoordinatesXY = null;
                JOptionPane.showMessageDialog(frameReference, "Simcos calculated");
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }

    /**
     * Calculate the simhub secondary distances.
     *
     * @param evt ActionEvent object.
     */
    private void simhubMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_simhubMenuItemActionPerformed
        Thread t = new Thread(new SimHubHelper());
        t.start();
    }//GEN-LAST:event_simhubMenuItemActionPerformed

    /**
     * Calculate the secondary mutual proximity distances.
     *
     * @param evt ActionEvent object.
     */
    private void mpMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mpMenuItemActionPerformed
        Thread t = new Thread(new MutualProximityHelper());
        t.start();
    }//GEN-LAST:event_mpMenuItemActionPerformed

    /**
     * Calculate the secondary local scaling distances.
     *
     * @param evt ActionEvent object.
     */
    private void localScalingItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_localScalingItemActionPerformed
        Thread t = new Thread(new LocalScalingHelper());
        t.start();
    }//GEN-LAST:event_localScalingItemActionPerformed

    /**
     * Calculate the secondary nicdm features.
     *
     * @param evt ActionEvent object.
     */
    private void nicdmItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nicdmItemActionPerformed
        Thread t = new Thread(new NICDMHelper());
        t.start();
    }//GEN-LAST:event_nicdmItemActionPerformed

    /**
     * Calculate the stats of the kNN sets and the neighbor occurrence
     * distribution.
     *
     * @param evt ActionEvent object.
     */
    private void neighborStatsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_neighborStatsItemActionPerformed
        Thread t = new Thread(new NSFStatsHelper());
        t.start();
    }//GEN-LAST:event_neighborStatsItemActionPerformed

    /**
     * This method selects the major image hub as the currently selected image.
     *
     * @param evt ActionEvent object.
     */
    private void majorHubSelectionItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_majorHubSelectionItemActionPerformed
        try {
            NeighborSetFinder nsf = getNSF().copy();
            nsf.recalculateStatsForSmallerK(neighborhoodSize);
            int[] hubness = nsf.getNeighborFrequencies();
            int[] maxWithIndex = ArrayUtil.maxWithIndex(hubness);
            int index = maxWithIndex[1];
            setSelectedImageForIndex(index);
            System.out.println("Selected hub index " + index);
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }//GEN-LAST:event_majorHubSelectionItemActionPerformed

    /**
     * Queries the data by an image from the represented collection.
     *
     * @param evt ActionEvent object.
     */
    private void collectionSearchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_collectionSearchButtonActionPerformed
        if (busyCalculating) {
            // If the system is working on something else, abort.
            return;
        }
        try {
            setQueryImageFromCollection();
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }//GEN-LAST:event_collectionSearchButtonActionPerformed

    /**
     * Select an image for the image query and extract its features.
     *
     * @param evt ActionEvent object.
     */
    private void imageBrowseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_imageBrowseButtonActionPerformed
        JFileChooser jfc = new JFileChooser(currentDirectory);
        jfc.setDialogTitle("Select Image Query");
        jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
        int rVal = jfc.showOpenDialog(ImageHubExplorer.this);
        if (rVal == JFileChooser.APPROVE_OPTION) {
            currentDirectory = jfc.getSelectedFile().getParentFile();
            try {
                File imageFile = jfc.getSelectedFile();
                queryImage = ImageIO.read(imageFile);
                queryImagePanel.setImage(queryImage);
                queryImagePanel.revalidate();
                queryImagePanel.repaint();
                if (codebook == null) {
                    // If the codebook has not been loaded, it is impossible to
                    // gemerate the quantized representation.
                    JOptionPane.showMessageDialog(frameReference, "First load the codebook", "Error message",
                            JOptionPane.ERROR_MESSAGE);
                    return;
                }
                String imgName = imageFile.getName();
                int pointIndex = imgName.lastIndexOf('.');
                String shortName = null;
                if (pointIndex != -1) {
                    shortName = imgName.substring(0, pointIndex);
                    shortName += ".pgm";
                } else {
                    shortName = shortName + ".pgm";
                }
                // Generate the PGM file for SiftWin SIFT feature extraction.
                File pgmFile = new File(workspace, "tmp" + File.separator + shortName);
                File siftTempFile = new File(workspace, "tmp" + File.separator + "siftTemp.key");
                ConvertJPGToPGM.convertFile(imageFile, pgmFile);
                SiftUtil.siftFile(pgmFile, siftTempFile, "");
                pgmFile.delete();
                // Load the extracted SIFT features.
                queryImageLFeat = SiftUtil.importFeaturesFromSift(siftTempFile);
                DataInstance queryImageRepAlmost = codebook.getDistributionForImageRepresentation(queryImageLFeat,
                        imageFile.getPath());
                // Get the color histogram.
                ColorHistogramVector cHist = new ColorHistogramVector();
                cHist.populateFromImage(queryImage, imgName);
                queryImageRep = new DataInstance();
                queryImageRep.fAttr = new float[queryImageRepAlmost.getNumFAtt() + cHist.getNumFAtt()];
                queryImageRep.iAttr = new int[queryImageRepAlmost.getNumIAtt()];
                queryImageRep.sAttr = new String[queryImageRepAlmost.getNumNAtt()];
                System.arraycopy(cHist.fAttr, 0, queryImageRep.fAttr, 0, cHist.getNumFAtt());
                for (int i = 0; i < queryImageRepAlmost.getNumFAtt(); i++) {
                    queryImageRep.fAttr[i + cHist.getNumFAtt()] = queryImageRepAlmost.fAttr[i];
                    if (queryImageRep.iAttr != null && queryImageRepAlmost.iAttr != null) {
                        queryImageRep.iAttr[i] = queryImageRepAlmost.iAttr[i];
                    }
                    if (queryImageRep.sAttr != null && queryImageRepAlmost.sAttr != null) {
                        queryImageRep.sAttr[i] = queryImageRepAlmost.sAttr[i];
                    }
                }
                // Reset the neighbor lists and the predictions in the query panel.
                queryNNPanel.removeAll();
                queryNNPanel.revalidate();
                queryNNPanel.repaint();

                classifierPredictionsPanel.removeAll();
                classifierPredictionsPanel.revalidate();
                classifierPredictionsPanel.repaint();
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        }
    }//GEN-LAST:event_imageBrowseButtonActionPerformed

    /**
     * Run the image query against the represented image dataset.
     *
     * @param evt ActionEvent object.
     */
    private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
        if (queryImageRep != null && queryImage != null) {
            imageQuery();
        }
    }//GEN-LAST:event_searchButtonActionPerformed

    /**
     * This method trains the classifier models on the represented image data.
     *
     * @param evt ActionEvent object.
     */
    private void trainModelsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_trainModelsItemActionPerformed
        trainModels();
    }//GEN-LAST:event_trainModelsItemActionPerformed

    /**
     * Perform re-ranking on the query result set.
     *
     * @param evt ActionEvent object.
     */
    private void reRankingButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_reRankingButtonActionPerformed
        if (busyCalculating || queryImage == null || queryImageNeighborDists == null) {
            return;
        }
        busyCalculating = true;
        try {
            // Get the neighbor occurrence profiles for the k of the query.
            NeighborSetFinder nsf = getNSF().copy();
            nsf.recalculateStatsForSmallerK(kQuery);
            int[] hubness = nsf.getNeighborFrequencies();
            int[] badHubness = nsf.getBadFrequencies();
            // Re-scale the distances to the neighbors.
            for (int i = 0; i < queryImageNeighborDists.length; i++) {
                if (hubness[queryImageNeighbors[i]] > 0) {
                    queryImageNeighborDists[i] *= ((float) (badHubness[queryImageNeighbors[i]]))
                            / ((float) (hubness[queryImageNeighbors[i]]));
                }
            }
            // Sort according to the new distances.
            int[] permutationIndexes = AuxSort.sortIndexedValue(queryImageNeighborDists, false);
            // Now permute the query neighbors.
            int[] qNeighbors = new int[queryImageNeighbors.length];
            for (int i = 0; i < queryImageNeighborDists.length; i++) {
                qNeighbors[i] = queryImageNeighbors[permutationIndexes[i]];
            }
            // Empty the query neighbors display.
            queryImageNeighbors = qNeighbors;
            queryNNPanel.removeAll();
            queryNNPanel.revalidate();
            queryNNPanel.repaint();
            // Insert elements into the query neighbor display according to the
            // re-ranked kNN set.
            for (int i = 0; i < queryImageNeighbors.length; i++) {
                BufferedImage thumb = thumbnails.get(queryImageNeighbors[i]);
                ImagePanelWithClass imgPan = new ImagePanelWithClass(classColors);
                imgPan.addMouseListener(new NeighborSelectionListener());
                imgPan.setImage(thumb, quantizedRepresentation.getLabelOf(queryImageNeighbors[i]),
                        queryImageNeighbors[i]);
                queryNNPanel.add(imgPan);
            }
            queryNNPanel.revalidate();
            queryNNPanel.repaint();
            // Update the predictions.
            if (trainedModels) {
                System.out.println("classifying");
                classifierPredictionsPanel.removeAll();
                classifierPredictionsPanel.revalidate();
                classifierPredictionsPanel.repaint();
                float[] trainingDists = new float[quantizedRepresentation.size()];
                for (int i = 0; i < queryImageNeighbors.length; i++) {
                    trainingDists[queryImageNeighbors[i]] = queryImageNeighborDists[i];
                }
                for (int i = 0; i < classifiers.length; i++) {
                    System.out.println("classification by" + classifierNameList[i]);
                    // Class affiliation prediction.
                    float[] prediction;
                    if (classifiers[i] instanceof NeighborPointsQueryUserInterface) {
                        prediction = ((NeighborPointsQueryUserInterface) (classifiers[i]))
                                .classifyProbabilistically(queryImageRep, trainingDists, queryImageNeighbors);
                    } else {
                        prediction = classifiers[i].classifyProbabilistically(queryImageRep);
                    }
                    ClassifierResultPanel cResPanel = new ClassifierResultPanel();
                    cResPanel.setResults(prediction, classifierNameList[i], classColors, classNames);
                    classifierPredictionsPanel.add(cResPanel);
                }
                classifierPredictionsPanel.revalidate();
                classifierPredictionsPanel.repaint();
            }
        } catch (Exception e) {
            System.err.println(e.getMessage());
        } finally {
            busyCalculating = false;
        }
    }//GEN-LAST:event_reRankingButtonActionPerformed

    /**
     * Perform multi-dimensional scaling data visualization.
     *
     * @param evt ActionEvent object.
     */
    private void mdsVisualizeItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mdsVisualizeItemActionPerformed
        if (busyCalculating) {
            // If the system is busy calculating something, abort.
            return;
        }
        try {
            busyCalculating = true;
            // In the internal representation in this library, the distance matrix
            // is represented as upper triangular. However, the MDS component
            // from MDSJ requires a full square distance matrix, so we have to
            // generate it here from the more compact representation that is
            // normally used.
            float[][] halfDist = getDistances();
            if (halfDist == null || halfDist.length == 0) {
                busyCalculating = false;
                return;
            }
            double[][] fullDist = new double[halfDist.length][halfDist.length];
            for (int i = 0; i < halfDist.length; i++) {
                fullDist[i][i] = 0;
                for (int j = i + 1; j < halfDist.length; j++) {
                    fullDist[i][j] = halfDist[i][j - i - 1];
                    fullDist[j][i] = halfDist[i][j - i - 1];
                }
            }
            double[][] resultsReversed = MDSJ.classicalScaling(fullDist);
            System.out.println("MDS performed.");
            // Get the calculated image coordinates.
            imageCoordinatesXY = new float[halfDist.length][2];
            float maxX = -Float.MAX_VALUE;
            float maxY = -Float.MAX_VALUE;
            float minX = Float.MAX_VALUE;
            float minY = Float.MAX_VALUE;
            for (int i = 0; i < halfDist.length; i++) {
                imageCoordinatesXY[i][0] = (float) resultsReversed[0][i];
                imageCoordinatesXY[i][1] = (float) resultsReversed[1][i];
                if (imageCoordinatesXY[i][0] > maxX) {
                    maxX = imageCoordinatesXY[i][0];
                }
                if (imageCoordinatesXY[i][0] < minX) {
                    minX = imageCoordinatesXY[i][0];
                }
                if (imageCoordinatesXY[i][1] > maxY) {
                    maxY = imageCoordinatesXY[i][1];
                }
                if (imageCoordinatesXY[i][1] < minY) {
                    minY = imageCoordinatesXY[i][1];
                }
            }
            // Re-scale the result to fit the window.
            for (int i = 0; i < halfDist.length; i++) {
                imageCoordinatesXY[i][0] = ((imageCoordinatesXY[i][0] - minX) / (maxX - minX))
                        * mdsCollectionPanel.getWidth();
                imageCoordinatesXY[i][1] = ((imageCoordinatesXY[i][1] - minY) / (maxY - minY))
                        * mdsCollectionPanel.getHeight();
            }
            System.out.println("Coordinates calculated.");
            // Find the major hubs and calculate the size of their visual display.
            // Offsets in case some bounding rectangles fall out of the MDS panel.
            float offX, offY;
            mdsBackgrounds = new BufferedImage[50];
            if (highestHubnesses != null) {
                float maxFrequency = ArrayUtil.max(highestHubnesses[neighborhoodSize - 1]);
                float[] thumbSizes = new float[highestHubnesses[neighborhoodSize - 1].length];
                ArrayList<Rectangle2D> bounds = new ArrayList<>(thumbSizes.length);
                ArrayList<ImagePanelWithClass> imgsMDS = new ArrayList<>(thumbSizes.length);
                for (int i = 0; i < thumbSizes.length; i++) {
                    // Get the display thumbnail size.
                    thumbSizes[i] = pointScale(highestHubnesses[neighborhoodSize - 1][i], maxFrequency,
                            minImageScale, maxImageScale);
                    // Calculate the offsets.
                    if (imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][0]
                            + thumbSizes[i] / 2 > mdsCollectionPanel.getWidth()) {
                        offX = (thumbSizes[i] / 2 - (mdsCollectionPanel.getWidth()
                                - imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][0]));
                    } else if (imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][0]
                            - thumbSizes[i] / 2 < 0) {
                        offX = imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][0];
                    } else {
                        offX = thumbSizes[i] / 2;
                    }
                    if (imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][1]
                            + thumbSizes[i] / 2 > mdsCollectionPanel.getHeight()) {
                        offY = (thumbSizes[i] / 2 - (mdsCollectionPanel.getHeight()
                                - imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][1]));
                    } else if (imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][1]
                            - thumbSizes[i] / 2 < 0) {
                        offY = imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][1];
                    } else {
                        offY = thumbSizes[i] / 2;
                    }
                    BufferedImage thumb = thumbnails.get(highestHubIndexes[neighborhoodSize - 1][i]);
                    ImagePanelWithClass imgPan = new ImagePanelWithClass(classColors);
                    imgPan.addMouseListener(new NeighborSelectionListener());
                    imgPan.setImage(thumb,
                            quantizedRepresentation.getLabelOf(highestHubIndexes[neighborhoodSize - 1][i]),
                            highestHubIndexes[neighborhoodSize - 1][i]);
                    imgsMDS.add(imgPan);
                    bounds.add(new Rectangle2D.Float(
                            imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][0] - offX,
                            imageCoordinatesXY[highestHubIndexes[neighborhoodSize - 1][i]][1] - offY, thumbSizes[i],
                            thumbSizes[i]));
                }
                mdsCollectionPanel.setImageSet(imgsMDS, bounds);
                // Set the background to the MDS screen.
                setMDSBackground();
                // Refresh.
                mdsCollectionPanel.revalidate();
                mdsCollectionPanel.repaint();
            }
        } catch (Exception e) {
            System.err.println(e.getMessage());
        } finally {
            busyCalculating = false;
        }
    }//GEN-LAST:event_mdsVisualizeItemActionPerformed

    /**
     * Add the currently selected image to the kNN graph for visualization.
     *
     * @param evt ActionEvent object.
     */
    private void addSelectedButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addSelectedButtonActionPerformed
        addSelectedImageToGraph();
    }//GEN-LAST:event_addSelectedButtonActionPerformed

    /**
     * Add the nearest neighbors of the currently selected image to the kNN
     * graph for visualization.
     *
     * @param evt ActionEvent object.
     */
    private void addNNsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addNNsButtonActionPerformed
        if (!this.neighborStatsCalculated || selectedImageHistory == null || selectedImageHistory.size() < 1) {
            return;
        }
        NeighborSetFinder nsf = getNSF();
        int[][] kneighbors = nsf.getKNeighbors();
        int index = selectedImageHistory.get(selectedImageIndexInHistory);
        int[] indexes = Arrays.copyOf(kneighbors[index], neighborhoodSize);
        addSelectedImagesToGraph(indexes);
    }//GEN-LAST:event_addNNsButtonActionPerformed

    /**
     * Add the reverse nearest neighbors of the currently selected image to the
     * kNN graph for visualization.
     *
     * @param evt ActionEvent object.
     */
    private void addRNNsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addRNNsButtonActionPerformed
        if (!this.neighborStatsCalculated || selectedImageHistory == null || selectedImageHistory.size() < 1) {
            return;
        }
        int index = selectedImageHistory.get(selectedImageIndexInHistory);
        int[] indexes = new int[rnnSetsAllK[neighborhoodSize - 1][index].size()];
        for (int i = 0; i < indexes.length; i++) {
            indexes[i] = rnnSetsAllK[neighborhoodSize - 1][index].get(i);
        }
        addSelectedImagesToGraph(indexes);
    }//GEN-LAST:event_addRNNsButtonActionPerformed

    /**
     * Remove the vertex from the kNN graph subset that is being visualized.
     *
     * @param evt ActionEvent object.
     */
    private void removeVertexButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeVertexButtonActionPerformed
        removeSelectedImageFromGraph();
    }//GEN-LAST:event_removeVertexButtonActionPerformed

    /**
     * This method loads the codebook profile from a file that contains the
     * visual word occurrence probabilities per class and is used for visual
     * word utility estimation.
     *
     * @param evt ActionEvent object.
     */
    private void loadCodebookProfileMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadCodebookProfileMenuItemActionPerformed
        BufferedReader br;
        JFileChooser jfc = new JFileChooser(currentDirectory);
        if (codebook == null) {
            JOptionPane.showMessageDialog(this, "no codebook loaded", "Error", JOptionPane.ERROR_MESSAGE);
            return;
        }
        jfc.setDialogTitle("Load Codebook Profiles: ");
        jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
        int rVal = jfc.showOpenDialog(ImageHubExplorer.this);
        if (rVal == JFileChooser.APPROVE_OPTION) {
            currentDirectory = jfc.getSelectedFile().getParentFile();
            codebookProfileFile = jfc.getSelectedFile();
            try {
                // Load the codebook profiles.
                br = new BufferedReader(new InputStreamReader(new FileInputStream(codebookProfileFile)));
                int size = Integer.parseInt(br.readLine());
                if (size != codebook.getSize()) {
                    throw new Exception("codebook profile size not equal to " + "codebook size");
                }
                codebookProfiles = new double[codebook.getSize()][numClasses];
                String line;
                String[] lineItems;
                float[] codeClassMax = new float[codebook.getSize()];
                float[] codeClassSums = new float[codebook.getSize()];
                for (int i = 0; i < size; i++) {
                    line = br.readLine();
                    lineItems = line.split(",");
                    if (lineItems.length != numClasses) {
                        throw new Exception("codebook profile class number " + "inappropriate " + lineItems.length
                                + " instead of " + numClasses);
                    }
                    // Keep track of the stats.
                    codeClassMax[i] = 0;
                    codeClassSums[i] = 0;
                    for (int c = 0; c < numClasses; c++) {
                        codebookProfiles[i][c] = Double.parseDouble(lineItems[c]);
                        codeClassMax[i] = (float) Math.max(codeClassMax[i], codebookProfiles[i][c]);
                        codeClassSums[i] += codebookProfiles[i][c];
                    }
                }
                // Calculate the goodness of each visual word.
                codebookGoodness = new float[codebook.getSize()];
                for (int codeIndex = 0; codeIndex < codebookGoodness.length; codeIndex++) {
                    if (codeClassSums[codeIndex] > 0) {
                        codebookGoodness[codeIndex] = codeClassMax[codeIndex] / codeClassSums[codeIndex];
                    } else {
                        codebookGoodness[codeIndex] = 0;
                    }
                }
                float maxGoodness = ArrayUtil.max(codebookGoodness);
                float minGoodness = ArrayUtil.min(codebookGoodness);
                for (int codeIndex = 0; codeIndex < codebookGoodness.length; codeIndex++) {
                    if ((maxGoodness - minGoodness) > 0) {
                        codebookGoodness[codeIndex] = (codebookGoodness[codeIndex] - minGoodness)
                                / (maxGoodness - minGoodness);
                    }
                }
                codebookProfPanels = new CodebookVectorProfilePanel[codebook.getSize()];
                for (int cInd = 0; cInd < codebook.getSize(); cInd++) {
                    CodebookVectorProfilePanel cProfPanel = new CodebookVectorProfilePanel();
                    cProfPanel.setResults(codebookProfiles[cInd], cInd, classColors, classNames);
                    cProfPanel.setPreferredSize(new Dimension(120, 120));
                    cProfPanel.setMinimumSize(new Dimension(120, 120));
                    cProfPanel.setMaximumSize(new Dimension(120, 120));
                    codebookProfPanels[cInd] = cProfPanel;
                }
                JOptionPane.showMessageDialog(frameReference, "Load completed");
            } catch (Exception e) {
                System.err.println(e.getMessage());
                codebookProfiles = null;
                codebookGoodness = null;
            }
        }
    }//GEN-LAST:event_loadCodebookProfileMenuItemActionPerformed

    /**
     * This method calculates the primary Tanimoto distances.
     *
     * @param evt ActionEvent object.
     */
    private void tanimotoMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tanimotoMenuItemActionPerformed
        if (!busyCalculating) {
            // If the system is currently performing calculations, abort the
            // call.
            busyCalculating = true;
            try {
                primaryCMet = CombinedMetric.FLOAT_TANIMOTO;
                primaryDMatFile = new File(workspace, "distancesNNSets" + File.separator
                        + primaryCMet.getFloatMetric().getClass().getName() + File.separator + "dMat.txt");
                boolean calcNSets = !(new File(primaryDMatFile.getParentFile(), "knnSets.txt")).exists();
                if (quantizedRepresentation != null && !quantizedRepresentation.isEmpty()
                        && !primaryDMatFile.exists()) {
                    // If they haven't already been calculated, calculate them
                    // now.
                    Thread t = new Thread(
                            new DistanceCalcHelperPrimary(calcNSets, primaryDMatFile.getParentFile()));
                    t.start();
                } else if (primaryDMatFile.exists()) {
                    // If they have already been calculated, just load them.
                    loadDistancesAndNeighbors(primaryDMatFile, PRIMARY_METRIC);
                }
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }//GEN-LAST:event_tanimotoMenuItemActionPerformed

    /**
     * This method calculates the symmetrized Kullback-Leibler divergence as a
     * distance measure.
     *
     * @param evt ActionEvent object.
     */
    private void klMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_klMenuItemActionPerformed
        if (!busyCalculating) {
            // If the system is currently performing calculations, abort the
            // call.
            busyCalculating = true;
            try {
                primaryCMet = CombinedMetric.FLOAT_KL;
                primaryDMatFile = new File(workspace, "distancesNNSets" + File.separator
                        + primaryCMet.getFloatMetric().getClass().getName() + File.separator + "dMat.txt");
                boolean calcNSets = !(new File(primaryDMatFile.getParentFile(), "knnSets.txt")).exists();
                if (quantizedRepresentation != null && !quantizedRepresentation.isEmpty()
                        && !primaryDMatFile.exists()) {
                    // If they haven't already been calculated, calculate them
                    // now.
                    Thread t = new Thread(
                            new DistanceCalcHelperPrimary(calcNSets, primaryDMatFile.getParentFile()));
                    t.start();
                } else if (primaryDMatFile.exists()) {
                    // If they have already been calculated, just load them.
                    loadDistancesAndNeighbors(primaryDMatFile, PRIMARY_METRIC);
                }
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }//GEN-LAST:event_klMenuItemActionPerformed

    /**
     * Calculate the primary Bray-Curtis distances.
     *
     * @param evt ActionEvent object.
     */
    private void bcMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bcMenuItemActionPerformed
        if (!busyCalculating) {
            // If the system is currently performing calculations, abort the
            // call.
            busyCalculating = true;
            try {
                primaryCMet = CombinedMetric.FLOAT_BRAY_CURTIS;
                primaryDMatFile = new File(workspace, "distancesNNSets" + File.separator
                        + primaryCMet.getFloatMetric().getClass().getName() + File.separator + "dMat.txt");
                boolean calcNSets = !(new File(primaryDMatFile.getParentFile(), "knnSets.txt")).exists();
                if (quantizedRepresentation != null && !quantizedRepresentation.isEmpty()
                        && !primaryDMatFile.exists()) {
                    // If they haven't already been calculated, calculate them
                    // now.
                    Thread t = new Thread(
                            new DistanceCalcHelperPrimary(calcNSets, primaryDMatFile.getParentFile()));
                    t.start();
                } else if (primaryDMatFile.exists()) {
                    // If they have already been calculated, just load them.
                    loadDistancesAndNeighbors(primaryDMatFile, PRIMARY_METRIC);
                }
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }//GEN-LAST:event_bcMenuItemActionPerformed

    /**
     * Calculate the primary Canberra distances.
     *
     * @param evt ActionEvent object.
     */
    private void canMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_canMenuItemActionPerformed
        if (!busyCalculating) {
            // If the system is currently performing calculations, abort the
            // call.
            busyCalculating = true;
            try {
                primaryCMet = CombinedMetric.FLOAT_CANBERRA;
                primaryDMatFile = new File(workspace, "distancesNNSets" + File.separator
                        + primaryCMet.getFloatMetric().getClass().getName() + File.separator + "dMat.txt");
                boolean calcNSets = !(new File(primaryDMatFile.getParentFile(), "knnSets.txt")).exists();
                if (quantizedRepresentation != null && !quantizedRepresentation.isEmpty()
                        && !primaryDMatFile.exists()) {
                    // If they haven't already been calculated, calculate them
                    // now.
                    Thread t = new Thread(
                            new DistanceCalcHelperPrimary(calcNSets, primaryDMatFile.getParentFile()));
                    t.start();
                } else if (primaryDMatFile.exists()) {
                    // If they have already been calculated, just load them.
                    loadDistancesAndNeighbors(primaryDMatFile, PRIMARY_METRIC);
                }
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }//GEN-LAST:event_canMenuItemActionPerformed

    /**
     * Get a screen capture of the kNN graph visualization and save the image to
     * a file.
     *
     * @param evt ActionEvent object.
     */
    private void graphScreenCaptureItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_graphScreenCaptureItemActionPerformed
        if (neighborGraphScrollPane != null) {
            BufferedImage bufImage = ScreenImage
                    .createImage((JComponent) neighborGraphScrollPane.getViewport().getComponent(0));
            try {
                File outFile;
                JFileChooser jfc = new JFileChooser(currentDirectory);
                jfc.setDialogTitle("Select file to save the component image: ");
                jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
                int rVal = jfc.showOpenDialog(ImageHubExplorer.this);
                if (rVal == JFileChooser.APPROVE_OPTION) {
                    currentDirectory = jfc.getSelectedFile().getParentFile();
                    outFile = jfc.getSelectedFile();
                    ImageIO.write(bufImage, "jpg", outFile);
                }
            } catch (Exception e) {
                System.err.println("problem writing file: " + e.getMessage());
            }
        }
    }//GEN-LAST:event_graphScreenCaptureItemActionPerformed

    /**
     * Get a screen capture of the MDS data visualization and save the image to
     * a file.
     *
     * @param evt ActionEvent object.
     */
    private void mdsScreenCaptureItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mdsScreenCaptureItemActionPerformed
        if (mdsCollectionPanel != null) {
            BufferedImage bufImage = ScreenImage.createImage((JComponent) mdsCollectionPanel);
            try {
                File outFile;
                JFileChooser jfc = new JFileChooser(currentDirectory);
                jfc.setDialogTitle("Select file to save the component image: ");
                jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
                int rVal = jfc.showOpenDialog(ImageHubExplorer.this);
                if (rVal == JFileChooser.APPROVE_OPTION) {
                    currentDirectory = jfc.getSelectedFile().getParentFile();
                    outFile = jfc.getSelectedFile();
                    ImageIO.write(bufImage, "jpg", outFile);
                }
            } catch (Exception e) {
                System.err.println("problem writing file: " + e.getMessage());
            }
        }
    }//GEN-LAST:event_mdsScreenCaptureItemActionPerformed

    /**
     * Remove all nodes from the kNN subgraph visualizations.
     *
     * @param evt ActionEvent object.
     */
    private void removeAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeAllButtonActionPerformed
        if (neighborStatsCalculated && neighborGraphs != null && selectedImageHistory != null
                && selectedImageHistory.size() > 0) {
            neighborGraphs = new DirectedGraph[50];
            graphsInit();
            System.gc();
            neighborGraphScrollPane.revalidate();
            neighborGraphScrollPane.repaint();
        }
    }//GEN-LAST:event_removeAllButtonActionPerformed

    /**
     * This method initiates the visual word utility assessment taking the
     * currently selected image as an example for examining the visual word
     * distribution and the utility of different image regions. It loads the
     * features from the disk if available and then starts the
     * QuantizedImageViewer component.
     *
     * @param evt ActionEvent object.
     */
    private void selSIFTmenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selSIFTmenuItemActionPerformed
        if (selectedImageHistory == null || selectedImageIndexInHistory >= selectedImageHistory.size()) {
            return;
        }
        // Ensure the codebook is properly loaded and the profiles are
        // available.
        if (codebook == null || codebookProfiles == null) {
            System.out.println("Both codebook and codebook profiles need to be" + " loaded");
            return;
        }
        // Get the image index within the image data.
        int index = selectedImageHistory.get(selectedImageIndexInHistory);
        String shortPath = imgPaths.get(index).substring((new File(workspace, "photos")).getPath().length(),
                imgPaths.get(index).length());
        File repDir = new File(workspace, "representation");
        File lFeatDir = new File(repDir, "raw_representation");
        int dotIndex = shortPath.lastIndexOf(".");
        String shortPathCutOff = shortPath.substring(0, dotIndex);
        // For files in SiftWin keypoint format.
        String shortPathKey = shortPathCutOff + ".key";
        // For files in the OpenCV format.
        String shortPathKeypoint = shortPathCutOff + ".kp";
        String shortPathDescriptor = shortPathCutOff + ".desc";
        LFeatRepresentation lFeatRep = null;
        File imgSIFTFile = new File(lFeatDir, shortPathKey);
        if (imgSIFTFile.exists()) {
            try {
                // Load the features from the disk
                lFeatRep = SiftUtil.importFeaturesFromSift(imgSIFTFile);
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        } else {
            File imgKPFile = new File(lFeatDir, shortPathKeypoint);
            File imgDescFile = new File(lFeatDir, shortPathDescriptor);
            if (!imgKPFile.exists() || !imgDescFile.exists()) {
                System.err.println("No supported local feature format detected" + "for the image.");
            } else {
                try {
                    // Load the features from the disk
                    lFeatRep = OpenCVFeatureIO.loadImageRepresentation(imgKPFile, imgDescFile);
                } catch (Exception e) {
                    System.err.println(e.getMessage());
                }
            }
        }
        if (lFeatRep == null || lFeatRep.isEmpty()) {
            return;
        }
        // Start examining the utility of different visual words on the image.
        BufferedImage originalImage = getPhoto(index);
        QuantizedImageViewer qiv = new QuantizedImageViewer(originalImage, lFeatRep, codebookGoodness, codebook,
                codebookProfiles, codebookProfPanels, classColors, classNames);
        qiv.setVisible(true);
    }//GEN-LAST:event_selSIFTmenuItemActionPerformed

    /**
     * This method determines the image thumbnail scale based on its occurrence
     * frequency.
     *
     * @param numOcc Float that is the image neighbor occurrence frequency.
     * @param MaxOcc Float that is the maximum occurrence frequency.
     * @param minScale Integer that is the minimal scale.
     * @param maxScale Integer that is the maximal scale.
     * @return Float that is the appropriate scale for the image thumbnail.
     * @throws Exception
     */
    private static float pointScale(float numOcc, float MaxOcc, int minScale, int maxScale) throws Exception {
        return minScale + (maxScale - minScale) * (float) Math.pow(((numOcc) / (MaxOcc)), 0.5);
    }

    /**
     * This method calculates and sets the background landscape for the MDS data
     * visualization. The landscape is set according the the good/bad hubness
     * densities of different regions in the panel, based on the MDS projection
     * of the data. The landscape is then softened by applying some blurring.
     */
    public void setMDSBackground() {
        if (mdsBackgrounds[neighborhoodSize - 1] == null) {
            // Get the width and height of the panel.
            int width = mdsCollectionPanel.getWidth();
            int height = mdsCollectionPanel.getHeight();
            // Cell size.
            int step = 100;
            int steppedWidth = width / step;
            if (width % step != 0) {
                steppedWidth++;
            }
            int steppedHeight = height / step;
            if (height % step != 0) {
                steppedHeight++;
            }
            // Get the kNN sets for the appropriate neighborhood size.
            NeighborSetFinder nsf = getNSF().copy();
            nsf.recalculateStatsForSmallerK(neighborhoodSize);
            int[] badHubness = nsf.getBadFrequencies();
            int[] goodHubness = nsf.getGoodFrequencies();
            int bucketX, bucketY;
            // Place all the projected data into the appropriate buckets in the
            // cell grid.
            // First initialize the buckets.
            ArrayList<Integer>[][] bucketedData = new ArrayList[steppedWidth][steppedHeight];
            for (int i = 0; i < steppedWidth; i++) {
                for (int j = 0; j < steppedHeight; j++) {
                    bucketedData[i][j] = new ArrayList<>(5);
                }
            }
            // Insert the data into the buckets.
            for (int i = 0; i < imageCoordinatesXY.length; i++) {
                bucketX = (int) (imageCoordinatesXY[i][0] / step);
                bucketY = (int) (imageCoordinatesXY[i][1] / step);
                if (bucketX >= bucketedData.length) {
                    continue;
                }
                if (bucketY >= bucketedData[bucketX].length) {
                    continue;
                }
                bucketedData[bucketX][bucketY].add(i);
            }
            int[] landscapeRaster = new int[width * height];
            // The goodness of a pixel.
            int greennessValue;
            // Good and bad hubness contributions.
            double ghFactor;
            double bhFactor;
            // Weight of a contribution.
            double weight;
            // Kernel width.
            double sigma = 0.05;
            int pX;
            int pY;
            int pIndex;
            for (int i = 0; i < landscapeRaster.length; i++) {
                pX = i % width;
                pY = i / width;
                bucketX = (pX) / step;
                bucketY = (pY) / step;
                ghFactor = 0;
                bhFactor = 0;
                for (int j = 0; j < bucketedData[bucketX][bucketY].size(); j++) {
                    pIndex = bucketedData[bucketX][bucketY].get(j);
                    weight = Math.min(Math.exp(
                            -sigma * ((imageCoordinatesXY[pIndex][0] - pX) * (imageCoordinatesXY[pIndex][0] - pX)
                                    + (imageCoordinatesXY[pIndex][1] - pY) * (imageCoordinatesXY[pIndex][1] - pY))),
                            1);
                    ghFactor += weight * goodHubness[pIndex];
                    bhFactor += weight * badHubness[pIndex];
                }
                if (ghFactor > 0 || bhFactor > 0) {
                    greennessValue = (int) (255 * ((ghFactor) / (ghFactor + bhFactor)));
                    landscapeRaster[i] = greennessValue << 8 | (255 - greennessValue) << 16;
                } else {
                    landscapeRaster[i] = 0x809080;
                }
            }
            // Now perform several passes of box blur to soften the landscape.
            BoxBlur bb = new BoxBlur(18);
            bb.blurPixels(landscapeRaster, new int[landscapeRaster.length], new Dimension(width, height));
            bb = new BoxBlur(9);
            bb.blurPixels(landscapeRaster, new int[landscapeRaster.length], new Dimension(width, height));
            bb = new BoxBlur(17);
            bb.blurPixels(landscapeRaster, new int[landscapeRaster.length], new Dimension(width, height));
            BufferedImage bckg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            bckg.setRGB(0, 0, width, height, landscapeRaster, 0, width);
            mdsBackgrounds[neighborhoodSize - 1] = bckg;
            mdsCollectionPanel.setBackgroundImage(bckg);
            // Refresh the display.
            mdsCollectionPanel.revalidate();
            mdsCollectionPanel.repaint();
        } else {
            // Load a landscape that was already calculated.
            mdsCollectionPanel.setBackgroundImage(mdsBackgrounds[neighborhoodSize - 1]);
            mdsCollectionPanel.revalidate();
            mdsCollectionPanel.repaint();
        }

    }

    /**
     * This class calculates the kNN and hubness-related stats for a general
     * data overview that are shown in the default visualization screen.
     */
    private class NSFStatsHelper implements Runnable {

        /**
         * The default constructor.
         */
        public NSFStatsHelper() {
        }

        @Override
        public void run() {
            if (busyCalculating) {
                // If the system is currently performing calculations, abort the
                // call.
                return;
            }
            busyCalculating = true;
            try {
                // Get the kNN sets.
                NeighborSetFinder nsf = getNSF();
                int k_max = 50;
                // Individual stats are calculates by the specialized objects.
                // Here is their initialization.
                HubnessAboveThresholdExplorer hte = new HubnessAboveThresholdExplorer(1, true, nsf);
                HubnessSkewAndKurtosisExplorer hske = new HubnessSkewAndKurtosisExplorer(nsf);
                HubnessExtremesGrabber heg = new HubnessExtremesGrabber(true, nsf);
                KNeighborEntropyExplorer knee = new KNeighborEntropyExplorer(nsf, numClasses);
                HubOrphanRegularPercentagesCalculator hubOrph = new HubOrphanRegularPercentagesCalculator(nsf,
                        k_max);
                BucketedOccDistributionGetter bucketGetter = new BucketedOccDistributionGetter(nsf, 50, 1);
                bucketedOccurrenceDistributions = bucketGetter.getBucketedDistributions();
                // Get the percentage of elements that occurs at least once, for
                // each neighborhood size.
                aboveZeroArray = hte.getThresholdPercentageArray();
                // Get the neighbor occurrence distribution properties.
                hske.calcSkewAndKurtosisArrays();
                skewArray = hske.getOccFreqsSkewnessArray();
                kurtosisArray = hske.getOccFreqsKurtosisArray();
                highestHubnesses = heg.getHubnessExtremesForKValues(numImagesDrawn);
                highestHubIndexes = heg.getExtremeIndexes();
                // Get the entropies of direct and reverse neighbor sets.
                knee.calculateAllKNNEntropyStats();
                kEntropies = knee.getDirectEntropyMeans();
                reverseKNNEntropies = knee.getReverseEntropyMeans();
                kEntropySkews = knee.getDirectEntropySkews();
                reverseKNNEntropySkews = knee.getReverseEntropySkews();
                badHubnessArray = nsf.getLabelMismatchPercsAllK();
                // Get the percentage of hubs, regulars and orphans in the data.
                hubOrph.calculatePTypePercs();
                hubPercs = hubOrph.getHubPercs();
                orphanPercs = hubOrph.getOrphanPercs();
                regularPercs = hubOrph.getRegularPercs();
                // Get the global class-to-class hubness distribution.
                globalClassToClasshubness = new float[k_max][][];
                for (int kTmp = 1; kTmp <= k_max; kTmp++) {
                    globalClassToClasshubness[kTmp - 1] = nsf.getGlobalClassToClassForKforFuzzy(kTmp, numClasses,
                            0.01f, true);
                }
                // Prepare the bucketed distribution data for display in charts.
                DefaultCategoryDataset hDistDataset = new DefaultCategoryDataset();
                for (int i = 0; i < bucketedOccurrenceDistributions[neighborhoodSize - 1].length; i++) {
                    hDistDataset.addValue(bucketedOccurrenceDistributions[neighborhoodSize - 1][i],
                            "Number of Examples", i + "");
                }
                JFreeChart chart = ChartFactory.createBarChart("Occurrence Frequency Distribution", "", "",
                        hDistDataset, PlotOrientation.VERTICAL, false, true, false);
                ChartPanel chartPanel = new ChartPanel(chart);
                chartPanel.setPreferredSize(new Dimension(440, 180));
                // Refresh the display.
                chartHoldingPanelOccDistribution.removeAll();
                chartHoldingPanelOccDistribution.add(chartPanel);
                chartHoldingPanelOccDistribution.revalidate();
                chartHoldingPanelOccDistribution.repaint();
                classesScrollPanel.setPreferredSize(new Dimension(760, 390 * numClasses));
                classesScrollPanel.setMaximumSize(new Dimension(760, 390 * numClasses));
                classesScrollPanel.setMinimumSize(new Dimension(760, 390 * numClasses));
                classesScrollPanel.removeAll();
                // Per-class top hubs, good hubs and bad hubs.
                classTopHubLists = new ArrayList[50][numClasses];
                classTopGoodHubsList = new ArrayList[50][numClasses];
                classTopBadHubsList = new ArrayList[50][numClasses];
                classHubnessArrValues = new ArrayList[50][numClasses];
                classHubnessArrGoodValues = new ArrayList[50][numClasses];
                classHubnessArrBadValues = new ArrayList[50][numClasses];
                // Make lists of instances belonging to each class.
                classImageIndexes = new ArrayList[numClasses];
                for (int c = 0; c < numClasses; c++) {
                    classImageIndexes[c] = new ArrayList<>(quantizedRepresentation.size());
                }
                int[][] permutationTotalHubness = new int[numClasses][];
                int[][] permutationGoodHubness = new int[numClasses][];
                int[][] permutationBadHubness = new int[numClasses][];
                int label;
                // Populate the class lists.
                for (int i = 0; i < quantizedRepresentation.size(); i++) {
                    label = quantizedRepresentation.getLabelOf(i);
                    classImageIndexes[label].add(i);
                }
                NeighborSetFinder nsfSmall = nsf.copy();
                rnnSetsAllK = new ArrayList[50][quantizedRepresentation.size()];
                occurrenceProfilesAllK = new float[50][quantizedRepresentation.size()][numClasses];
                for (int kTmp = 50; kTmp > 0; kTmp--) {
                    // For each neighborhood size.
                    nsfSmall.recalculateStatsForSmallerK(kTmp);
                    occurrenceProfilesAllK[kTmp - 1] = nsfSmall
                            .getDataClassNeighborRelationNonNormalized(neighborhoodSize, numClasses, false);
                    // Populate the reverse neighbor sets.
                    for (int i = 0; i < quantizedRepresentation.size(); i++) {
                        rnnSetsAllK[kTmp - 1][i] = new ArrayList<>(nsfSmall.getReverseNeighbors()[i].size());
                        for (int j = 0; j < nsfSmall.getReverseNeighbors()[i].size(); j++) {
                            rnnSetsAllK[kTmp - 1][i].add(nsfSmall.getReverseNeighbors()[i].get(j));
                        }
                    }
                    // Initialize the lists of top hubs, good hubs and bad hubs
                    // for each class.
                    for (int c = 0; c < numClasses; c++) {
                        classTopHubLists[kTmp - 1][c] = new ArrayList<>(quantizedRepresentation.size());
                        classTopGoodHubsList[kTmp - 1][c] = new ArrayList<>(quantizedRepresentation.size());
                        classTopBadHubsList[kTmp - 1][c] = new ArrayList<>(quantizedRepresentation.size());
                        classHubnessArrValues[kTmp - 1][c] = new ArrayList<>(quantizedRepresentation.size());
                        classHubnessArrGoodValues[kTmp - 1][c] = new ArrayList<>(quantizedRepresentation.size());
                        classHubnessArrBadValues[kTmp - 1][c] = new ArrayList<>(quantizedRepresentation.size());
                    }
                    // Get the good, bad and total hubness for each data
                    // instance.
                    int[] goodHubness = nsfSmall.getGoodFrequencies();
                    int[] badHubness = nsfSmall.getBadFrequencies();
                    int[] totalHubness = nsfSmall.getNeighborFrequencies();
                    // Insert the occurrence values into the class lists.
                    for (int i = 0; i < quantizedRepresentation.size(); i++) {
                        label = quantizedRepresentation.getLabelOf(i);
                        classHubnessArrValues[kTmp - 1][label].add(totalHubness[i]);
                        classHubnessArrGoodValues[kTmp - 1][label].add(goodHubness[i]);
                        classHubnessArrBadValues[kTmp - 1][label].add(badHubness[i]);
                    }
                    // Sort the class lists of good, bad and total hubness.
                    for (int c = 0; c < numClasses; c++) {
                        permutationTotalHubness[c] = AuxSort.sortIIndexedValue(classHubnessArrValues[kTmp - 1][c],
                                true);
                        permutationGoodHubness[c] = AuxSort
                                .sortIIndexedValue(classHubnessArrGoodValues[kTmp - 1][c], true);
                        permutationBadHubness[c] = AuxSort.sortIIndexedValue(classHubnessArrBadValues[kTmp - 1][c],
                                true);
                    }
                    // Populate the class lists of top total, good and bad hubs.
                    for (int c = 0; c < numClasses; c++) {
                        for (int i = 0; i < classImageIndexes[c].size(); i++) {
                            classTopHubLists[kTmp - 1][c]
                                    .add(classImageIndexes[c].get(permutationTotalHubness[c][i]));
                            classTopGoodHubsList[kTmp - 1][c]
                                    .add(classImageIndexes[c].get(permutationGoodHubness[c][i]));
                            classTopBadHubsList[kTmp - 1][c]
                                    .add(classImageIndexes[c].get(permutationBadHubness[c][i]));
                        }
                    }
                }
                // Calculate the distribution of point types in each class.
                int[][] kneighbors = nsf.getKNeighbors();
                classPTypes = new float[numClasses][4];
                int labelMatchCount;
                for (int i = 0; i < quantizedRepresentation.size(); i++) {
                    label = quantizedRepresentation.getLabelOf(i);
                    labelMatchCount = 0;
                    for (int j = 0; j < 5; j++) {
                        if (quantizedRepresentation.getLabelOf(kneighbors[i][j]) == label) {
                            labelMatchCount++;
                        }
                    }
                    switch (labelMatchCount) {
                    case 0:
                        // Outlier.
                        classPTypes[label][3]++;
                        break;
                    case 1:
                        // Rare point.
                        classPTypes[label][2]++;
                        break;
                    case 2:
                        // Borderline.
                        classPTypes[label][1]++;
                        break;
                    case 3:
                        // Borderline.
                        classPTypes[label][1]++;
                        break;
                    case 4:
                        // Safe.
                        classPTypes[label][0]++;
                        break;
                    case 5:
                        // Safe.
                        classPTypes[label][0]++;
                        break;
                    default:
                        classPTypes[label][0]++;
                    }

                }
                // Update the graphical elements.
                classStatsOverviews = new ClassHubsPanel[numClasses];
                for (int c = 0; c < numClasses; c++) {
                    ClassHubsPanel chp = new ClassHubsPanel(classColors[c], classNames[c]);
                    chp.setPointTypeDistribution(classPTypes[c]);
                    chp.revalidate();
                    chp.repaint();
                    classStatsOverviews[c] = chp;
                    JPanel hubsPanel = chp.getHubsPanel();
                    JPanel hubsPanelGood = chp.getGoodHubsPanel();
                    JPanel hubsPanelBad = chp.getBadHubsPanel();
                    // Handle the major hubs in the class.
                    for (int i = 0; i < Math.min(50, classImageIndexes[c].size()); i++) {
                        BufferedImage thumb = thumbnails.get(classTopHubLists[neighborhoodSize - 1][c].get(i));
                        ImagePanelWithClass imgPan = new ImagePanelWithClass(classColors);
                        imgPan.addMouseListener(new NeighborSelectionListener());
                        imgPan.setImage(thumb,
                                quantizedRepresentation
                                        .getLabelOf(classTopHubLists[neighborhoodSize - 1][c].get(i)),
                                classTopHubLists[neighborhoodSize - 1][c].get(i));
                        hubsPanel.add(imgPan);
                    }
                    hubsPanel.revalidate();
                    hubsPanel.repaint();
                    // Handle the major good hubs in the class.
                    for (int i = 0; i < Math.min(50, classImageIndexes[c].size()); i++) {
                        BufferedImage thumb = thumbnails.get(classTopGoodHubsList[neighborhoodSize - 1][c].get(i));
                        ImagePanelWithClass imgPan = new ImagePanelWithClass(classColors);
                        imgPan.addMouseListener(new NeighborSelectionListener());
                        imgPan.setImage(thumb,
                                quantizedRepresentation
                                        .getLabelOf(classTopGoodHubsList[neighborhoodSize - 1][c].get(i)),
                                classTopGoodHubsList[neighborhoodSize - 1][c].get(i));
                        hubsPanelGood.add(imgPan);
                    }
                    hubsPanelGood.revalidate();
                    hubsPanelGood.repaint();
                    // Handle the major bad hubs in the class.
                    for (int i = 0; i < Math.min(50, classImageIndexes[c].size()); i++) {
                        BufferedImage thumb = thumbnails.get(classTopBadHubsList[neighborhoodSize - 1][c].get(i));
                        ImagePanelWithClass imgPan = new ImagePanelWithClass(classColors);
                        imgPan.addMouseListener(new NeighborSelectionListener());
                        imgPan.setImage(thumb,
                                quantizedRepresentation
                                        .getLabelOf(classTopBadHubsList[neighborhoodSize - 1][c].get(i)),
                                classTopBadHubsList[neighborhoodSize - 1][c].get(i));
                        hubsPanelBad.add(imgPan);
                    }
                    hubsPanelBad.revalidate();
                    hubsPanelBad.repaint();
                    chp.revalidate();
                    chp.repaint();
                    classesScrollPanel.add(chp);
                }
                // Refresh the display.
                classesScrollPanel.revalidate();
                classesScrollPanel.repaint();
                classesScrollPane.revalidate();
                classesScrollPane.repaint();
                neighborStatsCalculated = true;
                graphsInit();
                JOptionPane.showMessageDialog(frameReference, "Neighbor Stats calculated.");
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }

    /**
     * This class calculates the secondary NICDM distances.
     */
    private class NICDMHelper implements Runnable {

        /**
         * The default constructor.
         */
        public NICDMHelper() {
        }

        @Override
        public void run() {
            if (busyCalculating) {
                // If the system is currently performing calculations, abort the
                // call.
                return;
            }
            busyCalculating = true;
            try {
                File pDir = primaryDMatFile.getParentFile();
                File sDir = new File(pDir, "secondary" + File.separator + "nicdm");
                File matFile = new File(sDir, "dMat.txt");
                if (matFile.exists() && secondaryLoadFlag) {
                    // If it already exists and the flag allows the load, load
                    // from the disk.
                    distMatrixSecondary = loadDMatFromFile(matFile);
                } else {
                    // Calculate the secondary distance matrix.
                    secondaryCMet = new NICDMCalculator(nsfPrimary);
                    distMatrixSecondary = ((NICDMCalculator) secondaryCMet).getTransformedDMatFromNSFPrimaryDMat();
                }
                // Load or calculate the secondary kNN sets.
                File neighborsFile = new File(sDir, "knnSets.txt");
                if (neighborsFile.exists() && secondaryLoadFlag) {
                    nsfSecondary = NeighborSetFinder.loadNSF(neighborsFile, quantizedRepresentation);
                    nsfSecondary.setDistances(distMatrixSecondary);
                } else {
                    nsfSecondary = new NeighborSetFinder(quantizedRepresentation, distMatrixSecondary,
                            secondaryCMet);
                    nsfSecondary.calculateNeighborSetsMultiThr(50, 8);
                }
                printDMatToFile(distMatrixSecondary, matFile);
                nsfSecondary.saveNeighborSets(neighborsFile);
                neighborStatsCalculated = false;
                imageCoordinatesXY = null;
                JOptionPane.showMessageDialog(frameReference, "NICDM calculated");
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }

    /**
     * This class calculates the secondary local scaling distances.
     */
    private class LocalScalingHelper implements Runnable {

        /**
         * The defautl constructor.
         */
        public LocalScalingHelper() {
        }

        @Override
        public void run() {
            if (busyCalculating) {
                // If the system is currently performing calculations, abort the
                // call.
                return;
            }
            busyCalculating = true;
            try {
                File pDir = primaryDMatFile.getParentFile();
                File sDir = new File(pDir, "secondary" + File.separator + "localscaling");
                File matFile = new File(sDir, "dMat.txt");
                if (matFile.exists() && secondaryLoadFlag) {
                    // If it already exists and the flag allows the load, load
                    // from the disk.
                    distMatrixSecondary = loadDMatFromFile(matFile);
                } else {
                    // Calculate the secondary distance matrix.
                    secondaryCMet = new LocalScalingCalculator(nsfPrimary);
                    distMatrixSecondary = ((LocalScalingCalculator) secondaryCMet)
                            .getTransformedDMatFromNSFPrimaryDMat();
                }
                // Load or calculate the secondary kNN sets.
                File neighborsFile = new File(sDir, "knnSets.txt");
                if (neighborsFile.exists() && secondaryLoadFlag) {
                    nsfSecondary = NeighborSetFinder.loadNSF(neighborsFile, quantizedRepresentation);
                    nsfSecondary.setDistances(distMatrixSecondary);
                } else {
                    nsfSecondary = new NeighborSetFinder(quantizedRepresentation, distMatrixSecondary,
                            secondaryCMet);
                    nsfSecondary.calculateNeighborSetsMultiThr(50, 8);
                }
                printDMatToFile(distMatrixSecondary, matFile);
                nsfSecondary.saveNeighborSets(neighborsFile);
                neighborStatsCalculated = false;
                imageCoordinatesXY = null;
                JOptionPane.showMessageDialog(frameReference, "Local scaling calculated");
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }

    /**
     * This class calculates the secondary mutual proximity distances.
     */
    private class MutualProximityHelper implements Runnable {

        /**
         * The default constructor.
         */
        public MutualProximityHelper() {
        }

        @Override
        public void run() {
            if (busyCalculating) {
                // If the system is currently performing calculations, abort the
                // call.
                return;
            }
            busyCalculating = true;
            try {
                File pDir = primaryDMatFile.getParentFile();
                File sDir = new File(pDir, "secondary" + File.separator + "mutualprox");
                File matFile = new File(sDir, "dMat.txt");
                if (matFile.exists() && secondaryLoadFlag) {
                    // If it already exists and the flag allows the load, load
                    // from the disk.
                    distMatrixSecondary = loadDMatFromFile(matFile);
                } else {
                    // Calculate the secondary distance matrix.
                    secondaryCMet = new MutualProximityCalculator(distMatrixPrimary, quantizedRepresentation,
                            primaryCMet);
                    distMatrixSecondary = ((MutualProximityCalculator) secondaryCMet).getTransformedDMat();
                }
                // Load or calculate the secondary kNN sets.
                File neighborsFile = new File(sDir, "knnSets.txt");
                if (neighborsFile.exists() && secondaryLoadFlag) {
                    nsfSecondary = NeighborSetFinder.loadNSF(neighborsFile, quantizedRepresentation);
                    nsfSecondary.setDistances(distMatrixSecondary);
                } else {
                    nsfSecondary = new NeighborSetFinder(quantizedRepresentation, distMatrixSecondary,
                            secondaryCMet);
                    nsfSecondary.calculateNeighborSetsMultiThr(50, 8);
                }
                printDMatToFile(distMatrixSecondary, matFile);
                nsfSecondary.saveNeighborSets(neighborsFile);
                neighborStatsCalculated = false;
                imageCoordinatesXY = null;
                JOptionPane.showMessageDialog(frameReference, "Mutual proximity calculated");
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }

    /**
     * This class calculates the secondary simhub distances, that are the
     * hubness-aware version of simcos distances.
     */
    private class SimHubHelper implements Runnable {

        /**
         * The default constructor.
         */
        public SimHubHelper() {
        }

        @Override
        public void run() {
            if (busyCalculating) {
                // If the system is currently performing calculations, abort the
                // call.
                return;
            }
            busyCalculating = true;
            try {
                File pDir = primaryDMatFile.getParentFile();
                File sDir = new File(pDir, "secondary" + File.separator + "simhub");
                File matFile = new File(sDir, "dMat.txt");
                if (matFile.exists() && secondaryLoadFlag) {
                    // If it already exists and the flag allows the load, load
                    // from the disk.
                    distMatrixSecondary = loadDMatFromFile(matFile);
                } else {
                    // Calculate the secondary distance matrix.
                    SharedNeighborFinder snf = new SharedNeighborFinder(nsfPrimary);
                    snf.setNumClasses(numClasses);
                    snf.setPrimaryMetricsCalculator(primaryCMet);
                    float theta = 0;
                    snf.obtainWeightsFromHubnessInformation(theta);
                    snf.countSharedNeighborsMultiThread(8);
                    // As we initially obtain the similarities, we have to
                    // convert them to distances.
                    distMatrixSecondary = snf.getSharedNeighborCounts();
                    for (int i = 0; i < distMatrixSecondary.length; i++) {
                        for (int j = 0; j < distMatrixSecondary[i].length; j++) {
                            distMatrixSecondary[i][j] = 50 - distMatrixSecondary[i][j];
                        }
                    }
                    // Initialize the secondary CombinedMetric object.
                    secondaryCMet = new SharedNeighborCalculator(snf,
                            SharedNeighborCalculator.WeightingType.HUBNESS_INFORMATION);

                }
                // Load or calculate the secondary kNN sets.
                File neighborsFile = new File(sDir, "knnSets.txt");
                if (neighborsFile.exists() && secondaryLoadFlag) {
                    nsfSecondary = NeighborSetFinder.loadNSF(neighborsFile, quantizedRepresentation);
                    nsfSecondary.setDistances(distMatrixSecondary);
                } else {
                    nsfSecondary = new NeighborSetFinder(quantizedRepresentation, distMatrixSecondary,
                            secondaryCMet);
                    nsfSecondary.calculateNeighborSetsMultiThr(50, 8);
                }
                printDMatToFile(distMatrixSecondary, matFile);
                nsfSecondary.saveNeighborSets(neighborsFile);
                neighborStatsCalculated = false;
                imageCoordinatesXY = null;
                JOptionPane.showMessageDialog(frameReference, "Simhub calculated");
            } catch (Exception e) {
                System.err.println(e.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }

    /**
     * Renderer for the class distribution.
     */
    private class ClassDistrRenderer extends StackedBarRenderer {

        @Override
        public Paint getItemPaint(int row, int col) {
            return classColors[col];
        }
    }

    /**
     * A custom table cell renderer for the class-to-class hubness matrix that
     * is similar to the kNN confusion matrix.
     */
    class ClassToClassHubnessMatrixRenderer extends DefaultTableCellRenderer {

        private float[][] classToClassHubnessMatrix;
        private int numClasses;

        /**
         * Initialization.
         *
         * @param classToClassHubnessMatrix float[][] that is the class-to-class
         * hubness matrix that is similar to the kNN confusion matrix.
         * @param numClasses Integer that is the number of classes.
         */
        public ClassToClassHubnessMatrixRenderer(float[][] classToClassHubnessMatrix, int numClasses) {
            this.numClasses = numClasses;
            this.classToClassHubnessMatrix = classToClassHubnessMatrix;
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                boolean hasFocus, int row, int column) {
            Component comp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            // We treat the diagonal elements differently from the rest.
            if (row == column) {
                comp.setBackground(new java.awt.Color(50,
                        50 + (int) Math.max(0, Math.min(205 * classToClassHubnessMatrix[row][column], 205)), 50));
            } else {
                comp.setBackground(new java.awt.Color(
                        50 + (int) Math.max(0, Math.min(205 * classToClassHubnessMatrix[row][column], 205)), 50,
                        50));
            }
            return comp;
        }
    }

    /**
     * This class performs the data import from the workspace.
     */
    private class ImportHelper implements Runnable {

        /**
         * Default constructor.
         */
        public ImportHelper() {
        }

        @Override
        public void run() {
            if (busyCalculating) {
                // If the system is currently performing calculations, abort the
                // call.
                return;
            }
            busyCalculating = true;
            try {
                // Directory where the quantized representation is, if provided.
                File qRepDir = new File(workspace, "representation" + File.separator + "quantized");
                // Get all the files in the directory.
                File[] reps = qRepDir.listFiles(new util.fileFilters.ARFFFileNameFilter());
                if (reps.length > 0) {
                    IOARFF pers = new IOARFF();
                    BufferedReader br = null;
                    try {
                        // Only take the first representation, as there is no
                        // way to know which one to prefer in case of multiple
                        // files.
                        quantizedRepresentation = pers.load(reps[0].getPath());
                        numClasses = quantizedRepresentation.countCategories();
                        numClassesLabelValue.setText((new Integer(numClasses)).toString());
                        // Get the class colors.
                        ColorPalette paleta = new ColorPalette();
                        classColors = paleta.getClassColorArray(numClasses);
                        collectionSizeLabelValue.setText((new Integer(quantizedRepresentation.size())).toString());
                        // Read the class names.
                        br = new BufferedReader(
                                new InputStreamReader(new FileInputStream(new File(workspace, "classNames.txt"))));
                        String s = br.readLine();
                        classNames = s.split(",");
                        for (int c = 0; c < numClasses; c++) {
                            classColorAndNamesPanel.add(new ClassNameAndColorPanel(
                                    classNames[Math.min(c, classNames.length - 1)], classColors[c]));
                        }
                        // Class name vector for setting the column names in
                        // the class-to-class hubness matrix.
                        Vector cNameVector = new Vector(classNames.length);
                        for (int c = 0; c < numClasses; c++) {
                            cNameVector.add(classNames[c]);
                        }
                        TableModel classToClassModel = new DefaultTableModel(numClasses, numClasses) {
                            @Override
                            public boolean isCellEditable(int row, int column) {
                                return false;
                            }
                        };
                        ((DefaultTableModel) classToClassModel).setColumnIdentifiers(cNameVector);
                        classHubnessTable.setModel(classToClassModel);
                        DefaultCategoryDataset cDistDataset = new DefaultCategoryDataset();
                        for (int c = 0; c < numClasses; c++) {
                            cDistDataset.addValue(quantizedRepresentation.getClassFrequencies()[c], "Size",
                                    classNames[c]);
                        }
                        for (int i = 0; i < numClasses; i++) {
                            classHubnessTable.getColumn(cNameVector.get(i)).setMinWidth(45);
                            classHubnessTable.getColumn(cNameVector.get(i)).setPreferredWidth(45);
                        }

                        classHubnessTable.setMinimumSize(new Dimension(45 * numClasses, 45 * numClasses));
                        // Visualize the class distribution.
                        JFreeChart chart = ChartFactory.createBarChart3D("Class Distribution", "Category", "Size",
                                cDistDataset, PlotOrientation.VERTICAL, false, true, false);
                        CategoryPlot plot = chart.getCategoryPlot();

                        plot.setRenderer(new ClassDistrRenderer());
                        ChartPanel chartPanel = new ChartPanel(chart);
                        chartPanel.setPreferredSize(new Dimension(440, 240));
                        classDistributionHolder.removeAll();
                        classDistributionHolder.add(chartPanel);
                        classDistributionHolder.revalidate();
                        classDistributionHolder.repaint();
                        classColorAndNamesPanel.validate();
                        classColorAndNamesPanel.repaint();
                        System.out.println("rep load successfull");
                    } catch (Exception e) {
                        System.err.println(e.getMessage());
                    } finally {
                        if (br != null) {
                            br.close();
                        }
                    }
                } else {
                    System.out.println("No quantized representation detected.");
                    return;
                }
                // Index of the feature containing the image path information.
                int pathFeatureIndex = quantizedRepresentation.getIndexForAttributeName("relative_path");
                File imageFile;
                // Load the thumbnails, a list of image paths, make a path map.
                images = new BufferedImage[quantizedRepresentation.size()];
                thumbnails = new ArrayList<>(quantizedRepresentation.size());
                imgPaths = new ArrayList<>(quantizedRepresentation.size());
                imgThumbPaths = new ArrayList<>(quantizedRepresentation.size());
                pathIndexMap = new HashMap<>(4 * quantizedRepresentation.size());
                pathIndexMapThumbnail = new HashMap<>(4 * quantizedRepresentation.size());
                for (int i = 0; i < quantizedRepresentation.size(); i++) {
                    imageFile = new File(workspace,
                            "photos" + quantizedRepresentation.getInstance(i).sAttr[pathFeatureIndex]);
                    imgPaths.add(imageFile.getPath());
                    pathIndexMap.put(imageFile.getPath(), i);
                    imageFile = new File(workspace,
                            "thumbnails" + quantizedRepresentation.getInstance(i).sAttr[pathFeatureIndex]);
                    imgThumbPaths.add(imageFile.getPath());
                    pathIndexMapThumbnail.put(imageFile.getPath(), i);
                    try {
                        thumbnails.add(ImageIO.read(imageFile));
                    } catch (Exception e) {
                        System.err.println(e.getMessage());
                        System.out.println(imageFile.getPath());
                    }
                }
                JOptionPane.showMessageDialog(frameReference, "Import completed");
            } catch (IOException | HeadlessException eSecond) {
                System.err.println(eSecond.getMessage());
            } finally {
                busyCalculating = false;
            }
        }
    }

    /**
     * Load distances and neighbor sets.
     *
     * @param matFile File that is the distance matrix file.
     * @param distanceModeIndicator Integer indicating whether to use primary or
     * secondary distance matrix load.
     */
    private void loadDistancesAndNeighbors(File matFile, int distanceModeIndicator) {
        File parDir = matFile.getParentFile();
        File neighborsFile = new File(parDir, "knnSets.txt");
        Thread t = new Thread(new DistancesNeighborsLoaderHelper(matFile, neighborsFile, distanceModeIndicator));
        t.start();
    }

    /**
     * This class loads the distances and neighbor sets from the files on the
     * disk.
     */
    private class DistancesNeighborsLoaderHelper implements Runnable {

        private File neighborsFile;
        private File matFile;
        private int distanceModeIndicator;

        /**
         * Initialization.
         *
         * @param matFile File that is the distance matrix file.
         * @param neighborsFile File that is the kNN file.
         * @param distanceModeIndicator Integer indicating whether this is a
         * primary or a secondary distance matrix load.
         */
        public DistancesNeighborsLoaderHelper(File matFile, File neighborsFile, int distanceModeIndicator) {
            this.matFile = matFile;
            this.neighborsFile = neighborsFile;
            this.distanceModeIndicator = distanceModeIndicator;
        }

        @Override
        public void run() {
            try {
                if (busyCalculating == true) {
                    // If the system is currently performing calculations,
                    // abort the call.
                    return;
                }
                busyCalculating = true;
                // Models are invalidated once the different kNN sets and
                // distances are loaded.
                trainedModels = false;
                if (distanceModeIndicator % 2 == PRIMARY_METRIC) {
                    // Primary matrix load.
                    if (matFile.exists()) {
                        distMatrixPrimary = loadDMatFromFile(matFile);
                    }
                    // Load or calculate the neighbors.
                    if (neighborsFile.exists()) {
                        nsfPrimary = NeighborSetFinder.loadNSF(neighborsFile, quantizedRepresentation);
                        nsfPrimary.setDistances(distMatrixPrimary);
                    } else {
                        nsfPrimary = new NeighborSetFinder(quantizedRepresentation, distMatrixPrimary, primaryCMet);
                        nsfPrimary.calculateNeighborSetsMultiThr(50, 8);
                    }
                } else {
                    // Secondary distance load.
                    if (matFile.exists()) {
                        distMatrixSecondary = loadDMatFromFile(matFile);
                    }
                    // Load or calculate the neighbors.
                    if (neighborsFile.exists()) {
                        nsfSecondary = NeighborSetFinder.loadNSF(neighborsFile, quantizedRepresentation);
                        nsfSecondary.setDistances(distMatrixSecondary);
                    } else {
                        if (secondaryCMet == null) {
                            secondaryCMet = new MutualProximityCalculator(distMatrixPrimary,
                                    quantizedRepresentation, primaryCMet);
                        }
                        nsfSecondary = new NeighborSetFinder(quantizedRepresentation, distMatrixSecondary,
                                secondaryCMet);
                        nsfSecondary.calculateNeighborSetsMultiThr(50, 8);
                    }
                }
                rnnSetsAllK = new ArrayList[50][quantizedRepresentation.size()];

                // Update the occurrence profiles and the reverse neighbor
                // lists.
                int[][] kneighbors = getNSF().getKNeighbors();
                int size = quantizedRepresentation.size();
                occurrenceProfilesAllK = new float[50][quantizedRepresentation.size()][numClasses];
                for (int j = 0; j < size; j++) {
                    rnnSetsAllK[0][j] = new ArrayList<>(2);
                }
                for (int j = 0; j < size; j++) {
                    rnnSetsAllK[0][kneighbors[j][0]].add(j);
                    occurrenceProfilesAllK[0][kneighbors[j][0]][quantizedRepresentation.getLabelOf(j)]++;
                }
                // For all neighborhood sizes.
                for (int kTmp = 1; kTmp < 50; kTmp++) {
                    if (kneighbors[0].length >= kTmp + 1) {
                        for (int j = 0; j < size; j++) {
                            rnnSetsAllK[kTmp][j] = new ArrayList<>(2 * (kTmp + 1));
                            rnnSetsAllK[kTmp][j].addAll(rnnSetsAllK[kTmp - 1][j]);
                            occurrenceProfilesAllK[kTmp][j] = Arrays.copyOf(occurrenceProfilesAllK[kTmp - 1][j],
                                    numClasses);
                        }
                        for (int j = 0; j < size; j++) {
                            rnnSetsAllK[kTmp][kneighbors[j][kTmp]].add(j);
                            occurrenceProfilesAllK[kTmp][kneighbors[j][kTmp]][quantizedRepresentation
                                    .getLabelOf(j)]++;
                        }
                    } else {
                        for (int j = 0; j < size; j++) {
                            rnnSetsAllK[kTmp][j] = new ArrayList<>(1);
                        }
                    }
                }
                // Reinitialize.
                aboveZeroArray = null;
                skewArray = null;
                kurtosisArray = null;
                highestHubnesses = null;
                highestHubIndexes = null;
                kEntropies = null;
                reverseKNNEntropies = null;
                kEntropySkews = null;
                reverseKNNEntropySkews = null;
                badHubnessArray = null;
                globalClassToClasshubness = null;
                hubPercs = null;
                orphanPercs = null;
                regularPercs = null;
                graphsDelete();
                for (int c1 = 0; c1 < numClasses; c1++) {
                    for (int c2 = 0; c2 < numClasses; c2++) {
                        classHubnessTable.setValueAt(" ", c1, c2);
                    }
                }
                percAboveLabelValue.setText("...");
                skewnessLabelValue.setText("...");
                kurtosisLabelValue.setText("...");
                majorDegLabelValue.setText("...");
                nkEntropySkewnessValues.setText("...");
                nkEntropyLabelValue.setText("...");
                rnkEntropySkewnessValue.setText("...");
                rnkEntropyValue.setText("...");
                badHubnessLabelValue.setText("...");
                imageCoordinatesXY = null;
                JOptionPane.showMessageDialog(frameReference, "Distances properly loaded.");
            } catch (Exception e) {
                System.err.println(e.getMessage());
                JOptionPane.showMessageDialog(frameReference, "An error occurred: " + e.getMessage(),
                        "Error message", JOptionPane.ERROR_MESSAGE);
            } finally {
                busyCalculating = false;
            }
        }
    }

    /**
     * @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(ImageHubExplorer.class.getName()).log(java.util.logging.Level.SEVERE,
                    null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(ImageHubExplorer.class.getName()).log(java.util.logging.Level.SEVERE,
                    null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(ImageHubExplorer.class.getName()).log(java.util.logging.Level.SEVERE,
                    null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(ImageHubExplorer.class.getName()).log(java.util.logging.Level.SEVERE,
                    null, ex);
        }
        //</editor-fold>

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

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton addNNsButton;
    private javax.swing.JButton addRNNsButton;
    private javax.swing.JButton addSelectedButton;
    private javax.swing.JLabel badHubnessLabelTxt;
    private javax.swing.JLabel badHubnessLabelValue;
    private javax.swing.JMenuItem bcMenuItem;
    private javax.swing.JScrollPane cNamesScrollPane;
    private javax.swing.JMenuItem canMenuItem;
    private javax.swing.JPanel chartHoldingPanelOccDistribution;
    private javax.swing.JPanel classColorAndNamesPanel;
    private javax.swing.JPanel classDistributionHolder;
    private javax.swing.JTable classHubnessTable;
    private javax.swing.JPanel classPanel;
    private javax.swing.JScrollPane classesScrollPane;
    private javax.swing.JPanel classesScrollPanel;
    private javax.swing.JMenu classificationMenu;
    private javax.swing.JPanel classifierPredictionsPanel;
    private javax.swing.JMenu codebookMenu;
    private javax.swing.JMenu collectionMenu;
    private javax.swing.JButton collectionSearchButton;
    private javax.swing.JLabel collectionSizeLabelTxt;
    private javax.swing.JLabel collectionSizeLabelValue;
    private javax.swing.JScrollPane confusionMatScrollPane;
    private javax.swing.JMenu dMatrixMenu;
    private javax.swing.JPanel dataMainPanel;
    private javax.swing.JMenuItem distCalcCosineItem;
    private javax.swing.JMenuItem distCalcEuclideanItem;
    private javax.swing.JMenu distCalculateMenu;
    private javax.swing.JMenuItem distImportItem;
    private javax.swing.JMenu editMenu;
    private javax.swing.JMenuItem graphScreenCaptureItem;
    private javax.swing.JLabel hRelatedPropTxt;
    private javax.swing.JTabbedPane hubTab;
    private javax.swing.JLabel hubsLabelTxt;
    private javax.swing.JLabel hubsLabelValue;
    private javax.swing.JButton imageBrowseButton;
    private javax.swing.JMenuItem importItem;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JScrollPane jScrollPane4;
    private javax.swing.JScrollPane jScrollPane5;
    private javax.swing.JTextField jTextField1;
    private javax.swing.JSlider kSelectionSlider;
    private javax.swing.JMenuItem klMenuItem;
    private javax.swing.JLabel kurtosisLabelTxt;
    private javax.swing.JLabel kurtosisLabelValue;
    private javax.swing.JMenuItem loadCodebookItem;
    private javax.swing.JMenuItem loadCodebookProfileMenuItem;
    private javax.swing.JMenuItem loadSecondaryDistancesItem;
    private javax.swing.JMenuItem localScalingItem;
    private javax.swing.JLabel majorDegLabelTxt;
    private javax.swing.JLabel majorDegLabelValue;
    private javax.swing.JMenuItem majorHubSelectionItem;
    private javax.swing.JMenuItem manhattanDistItem;
    private gui.images.ImagesDisplayPanel mdsCollectionPanel;
    private javax.swing.JMenuItem mdsScreenCaptureItem;
    private javax.swing.JScrollPane mdsScrollPane;
    private javax.swing.JMenuItem mdsVisualizeItem;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JMenu metricLearningMenu;
    private javax.swing.JMenuItem mpMenuItem;
    private javax.swing.JScrollPane neighborGraphScrollPane;
    private javax.swing.JPanel neighborPanel;
    private javax.swing.JMenuItem neighborStatsItem;
    private javax.swing.JMenuItem nextMenuItem;
    private javax.swing.JLabel nhSizeLabelTxt;
    private javax.swing.JMenuItem nicdmItem;
    private javax.swing.JLabel nkEntropyLabelTxt;
    private javax.swing.JLabel nkEntropyLabelValue;
    private javax.swing.JLabel nkEntropySkewnessTxt;
    private javax.swing.JLabel nkEntropySkewnessValues;
    private javax.swing.JPanel nnPanel;
    private javax.swing.JLabel nnScrollLabelTxt;
    private javax.swing.JScrollPane nnScrollPane;
    private javax.swing.JLabel noccProfLabelTxt;
    private javax.swing.JLabel numClassesLabelTxt;
    private javax.swing.JLabel numClassesLabelValue;
    private javax.swing.JPanel occProfileChartHolder;
    private javax.swing.JLabel orphansLabelTxt;
    private javax.swing.JLabel orphansLabelValue;
    private javax.swing.JLabel percAboveLabelTxt;
    private javax.swing.JLabel percAboveLabelValue;
    private javax.swing.JLabel prClassLabelTxt;
    private javax.swing.JScrollPane prClassScrollPane;
    private javax.swing.JMenuItem previousMenuItem;
    private gui.images.ImagePanel queryImagePanel;
    private javax.swing.JPanel queryNNPanel;
    private javax.swing.JScrollPane queryNNScrollPane;
    private javax.swing.JLabel queryQTextLabelTxt;
    private javax.swing.JButton reRankingButton;
    private javax.swing.JLabel regularLabelTxt;
    private javax.swing.JLabel regularLabelValue;
    private javax.swing.JButton removeAllButton;
    private javax.swing.JButton removeVertexButton;
    private javax.swing.JLabel rnkEntropySkewnessTxt;
    private javax.swing.JLabel rnkEntropySkewnessValue;
    private javax.swing.JLabel rnkEntropyValue;
    private javax.swing.JPanel rnnPanel;
    private javax.swing.JLabel rnnScrollLabelTxt;
    private javax.swing.JScrollPane rnnScrollPane;
    private javax.swing.JMenu screenCaptureMenu;
    private javax.swing.JButton searchButton;
    private javax.swing.JPanel searchPanel;
    private javax.swing.JLabel searchQLabelTxt;
    private javax.swing.JMenu secondaryMetricMenu;
    private javax.swing.JMenu selImageMenu;
    private javax.swing.JMenuItem selImgPathMenuItem;
    private javax.swing.JMenuItem selSIFTmenuItem;
    private javax.swing.JLabel selectedImageLabelClass;
    private javax.swing.JLabel selectedImageLabelClassNeighbor;
    private javax.swing.JLabel selectedImageLabelClassNeighborMain;
    private javax.swing.JLabel selectedImageLabelSearch;
    private gui.images.ImagePanel selectedImagePanelClass;
    private gui.images.ImagePanel selectedImagePanelClassNeighbor;
    private gui.images.ImagePanel selectedImagePanelClassNeighborMain;
    private gui.images.ImagePanel selectedImagePanelSearch;
    private javax.swing.JLabel selectedImagePathLabelClass;
    private javax.swing.JLabel selectedImagePathLabelClassNeighbor;
    private javax.swing.JLabel selectedImagePathLabelClassNeighborMain;
    private javax.swing.JLabel selectedImagePathLabelSearch;
    private javax.swing.JLabel simResLabelTxt;
    private javax.swing.JMenuItem simcosMenuItem;
    private javax.swing.JMenuItem simhubMenuItem;
    private javax.swing.JLabel skewnessLabelValue;
    private javax.swing.JLabel skewnwessLabelTxt;
    private javax.swing.JMenuItem tanimotoMenuItem;
    private javax.swing.JMenuItem trainModelsItem;
    private javax.swing.JLabel workspaceLabelTxt;
    private javax.swing.JLabel workspaceLabelValue;
    private javax.swing.JMenuItem workspaceMenuItem;
    // End of variables declaration//GEN-END:variables
}