Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package gov.nasa.jpl.memex.pooledtimeseries; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import java.util.logging.Logger; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.TrueFileFilter; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.opencv.core.Core; import org.opencv.core.Mat; import org.opencv.core.MatOfPoint2f; import org.opencv.core.Point; import org.opencv.core.Size; import org.opencv.highgui.VideoCapture; import org.opencv.imgproc.Imgproc; import org.opencv.video.Video; /** * * Pooled Time Series Similarity Metric. * */ @SuppressWarnings({ "static-access", "deprecation" }) public class PoT { public static int frame_width = 320; public static int frame_height = 240; private static String outputFile = "similarity.txt"; private static enum OUTPUT_FORMATS { TXT, JSON } private static OUTPUT_FORMATS outputFormat = OUTPUT_FORMATS.TXT; private static final Logger LOG = Logger.getLogger(PoT.class.getName()); public static void main(String[] args) { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); Option fileOpt = OptionBuilder.withArgName("file").hasArg().withLongOpt("file") .withDescription("Path to a single file").create('f'); Option dirOpt = OptionBuilder.withArgName("directory").hasArg().withLongOpt("dir") .withDescription("A directory with image files in it").create('d'); Option helpOpt = OptionBuilder.withLongOpt("help").withDescription("Print this message.").create('h'); Option pathFileOpt = OptionBuilder.withArgName("path file").hasArg().withLongOpt("pathfile") .withDescription( "A file containing full absolute paths to videos. Previous default was memex-index_temp.txt") .create('p'); Option outputFileOpt = OptionBuilder.withArgName("output file").withLongOpt("outputfile").hasArg() .withDescription("File containing similarity results. Defaults to ./similarity.txt").create('o'); Option jsonOutputFlag = OptionBuilder.withArgName("json output").withLongOpt("json") .withDescription("Set similarity output format to JSON. Defaults to .txt").create('j'); Option similarityFromFeatureVectorsOpt = OptionBuilder .withArgName("similarity from FeatureVectors directory") .withLongOpt("similarityFromFeatureVectorsDirectory").hasArg() .withDescription("calculate similarity matrix from given directory of feature vectors").create('s'); Options options = new Options(); options.addOption(dirOpt); options.addOption(pathFileOpt); options.addOption(fileOpt); options.addOption(helpOpt); options.addOption(outputFileOpt); options.addOption(jsonOutputFlag); options.addOption(similarityFromFeatureVectorsOpt); // create the parser CommandLineParser parser = new GnuParser(); try { // parse the command line arguments CommandLine line = parser.parse(options, args); String directoryPath = null; String pathFile = null; String singleFilePath = null; String similarityFromFeatureVectorsDirectory = null; ArrayList<Path> videoFiles = null; if (line.hasOption("dir")) { directoryPath = line.getOptionValue("dir"); } if (line.hasOption("pathfile")) { pathFile = line.getOptionValue("pathfile"); } if (line.hasOption("file")) { singleFilePath = line.getOptionValue("file"); } if (line.hasOption("outputfile")) { outputFile = line.getOptionValue("outputfile"); } if (line.hasOption("json")) { outputFormat = OUTPUT_FORMATS.JSON; } if (line.hasOption("similarityFromFeatureVectorsDirectory")) { similarityFromFeatureVectorsDirectory = line .getOptionValue("similarityFromFeatureVectorsDirectory"); } if (line.hasOption("help") || (line.getOptions() == null || (line.getOptions() != null && line.getOptions().length == 0)) || (directoryPath != null && pathFile != null && !directoryPath.equals("") && !pathFile.equals(""))) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("pooled_time_series", options); System.exit(1); } if (directoryPath != null) { File dir = new File(directoryPath); List<File> files = (List<File>) FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE); videoFiles = new ArrayList<Path>(files.size()); for (File file : files) { String filePath = file.toString(); // When given a directory to load videos from we need to ensure that we // don't try to load the of.txt and hog.txt intermediate result files // that results from previous processing runs. if (!filePath.contains(".txt")) { videoFiles.add(file.toPath()); } } LOG.info("Added " + videoFiles.size() + " video files from " + directoryPath); } if (pathFile != null) { Path list_file = Paths.get(pathFile); videoFiles = loadFiles(list_file); LOG.info("Loaded " + videoFiles.size() + " video files from " + pathFile); } if (singleFilePath != null) { Path singleFile = Paths.get(singleFilePath); LOG.info("Loaded file: " + singleFile); videoFiles = new ArrayList<Path>(1); videoFiles.add(singleFile); } if (similarityFromFeatureVectorsDirectory != null) { File dir = new File(similarityFromFeatureVectorsDirectory); List<File> files = (List<File>) FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE); videoFiles = new ArrayList<Path>(files.size()); for (File file : files) { String filePath = file.toString(); // We need to load only the *.of.txt and *.hog.txt values if (filePath.endsWith(".of.txt")) { videoFiles.add(file.toPath()); } if (filePath.endsWith(".hog.txt")) { videoFiles.add(file.toPath()); } } LOG.info("Added " + videoFiles.size() + " feature vectors from " + similarityFromFeatureVectorsDirectory); evaluateSimilarity(videoFiles, 1); } else { evaluateSimilarity(videoFiles, 1); } LOG.info("done."); } catch (ParseException exp) { // oops, something went wrong System.err.println("Parsing failed. Reason: " + exp.getMessage()); } } public static void evaluateSimilarity(ArrayList<Path> files, int save_mode) { // PoT level set ArrayList<double[]> tws = getTemporalWindows(4); // computing feature vectors ArrayList<FeatureVector> fv_list = new ArrayList<FeatureVector>(); for (int k = 0; k < files.size(); k++) { try { LOG.fine(files.get(k).toString()); ArrayList<double[][]> multi_series = new ArrayList<double[][]>(); Path file = files.get(k); // optical flow descriptors String series_name1 = file.toString(); if ((!series_name1.endsWith(".of.txt")) && (!series_name1.endsWith(".hog.txt"))) { series_name1 += ".of.txt"; } Path series_path1 = Paths.get(series_name1); double[][] series1; if (save_mode == 0) { series1 = getOpticalTimeSeries(file, 5, 5, 8); saveVectors(series1, series_path1); } else { series1 = loadTimeSeries(series_path1); } multi_series.add(series1); // gradients descriptors String series_name2 = file.toString(); if ((!series_name2.endsWith(".hog.txt")) && (!series_name2.endsWith(".of.txt"))) { series_name2 += ".hog.txt"; } Path series_path2 = Paths.get(series_name2); double[][] series2; if (save_mode == 0) { series2 = getGradientTimeSeries(file, 5, 5, 8); saveVectors(series2, series_path2); } else { series2 = loadTimeSeries(series_path2); } multi_series.add(series2); // computing features from series of descriptors FeatureVector fv = new FeatureVector(); for (int i = 0; i < multi_series.size(); i++) { fv.feature.add(computeFeaturesFromSeries(multi_series.get(i), tws, 1)); fv.feature.add(computeFeaturesFromSeries(multi_series.get(i), tws, 2)); fv.feature.add(computeFeaturesFromSeries(multi_series.get(i), tws, 5)); } LOG.info((k + 1) + "/" + files.size() + " files done. " + "Finished processing file: " + file.getFileName()); fv_list.add(fv); } catch (PoTException e) { LOG.severe("PoTException occurred: " + e.message + ": Skipping file " + files.get(k)); continue; } } double[][] similarities = calculateSimilarities(fv_list); writeSimilarityOutput(files, similarities); } public static double[][] calculateSimilarities(ArrayList<FeatureVector> fv_list) { // feature vector similarity measure if (fv_list.size() < 1) { LOG.info("Feature Vector list is empty. Nothing to calculate. Exiting..."); System.exit(1); } double[] mean_dists = new double[fv_list.get(0).numDim()]; for (int i = 0; i < fv_list.get(0).numDim(); i++) mean_dists[i] = meanChiSquareDistances(fv_list, i); System.out.print("mean-chi-square-distances: "); for (int i = 0; i < fv_list.get(0).numDim(); i++) System.out.format("%f ", mean_dists[i]); System.out.println(""); double[][] sims = new double[fv_list.size()][fv_list.size()]; for (int i = 0; i < fv_list.size(); i++) { for (int j = 0; j < fv_list.size(); j++) { sims[i][j] = kernelDistance(fv_list.get(i), fv_list.get(j), mean_dists); } } return sims; } private static void writeSimilarityOutput(ArrayList<Path> files, double[][] similarities) { if (outputFormat == OUTPUT_FORMATS.TXT) { writeSimilarityToTextFile(similarities); } else if (outputFormat == OUTPUT_FORMATS.JSON) { writeSimilarityToJSONFile(files, similarities); } else { LOG.severe("Invalid output format. Skipping similarity dump."); } } private static void writeSimilarityToTextFile(double[][] similarities) { try { FileOutputStream fos = new FileOutputStream(outputFile); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos)); for (int i = 0; i < similarities.length; i++) { for (int j = 0; j < similarities[0].length; j++) { writer.write(String.format("%f,", similarities[i][j])); } writer.newLine(); } writer.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } private static void writeSimilarityToJSONFile(ArrayList<Path> files, double[][] similarities) { JSONObject root_json_obj = new JSONObject(); for (int i = 0; i < similarities.length; i++) { JSONObject fileJsonObj = new JSONObject(); for (int j = 0; j < similarities[0].length; j++) { fileJsonObj.put(files.get(j).getFileName(), similarities[i][j]); } root_json_obj.put(files.get(i).getFileName(), fileJsonObj); } try { outputFile = outputFile.substring(0, outputFile.lastIndexOf('.')) + ".json"; FileWriter file = new FileWriter(outputFile); file.write(root_json_obj.toJSONString()); file.flush(); file.close(); } catch (IOException e) { e.printStackTrace(); } } public static ArrayList<Path> loadFiles(Path list_file) { ArrayList<Path> filenames = new ArrayList<Path>(); try (InputStream in = Files.newInputStream(list_file); BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { String line = null; while ((line = reader.readLine()) != null) { filenames.add(Paths.get(line)); } } catch (IOException x) { System.err.println(x); } return filenames; } public static double[][] getOpticalTimeSeries(Path filename, int w_d, int h_d, int o_d) throws PoTException { ArrayList<double[][][]> hists = getOpticalHistograms(filename, w_d, h_d, o_d); double[][] vectors = new double[hists.size()][]; for (int i = 0; i < hists.size(); i++) { vectors[i] = histogramToVector(hists.get(i)); } return vectors; } static double[] histogramToVector(double[][][] hist) { int d1 = hist.length; int d2 = hist[0].length; int d3 = hist[0][0].length; double[] vector = new double[d1 * d2 * d3]; for (int i = 0; i < d1; i++) { for (int j = 0; j < d2; j++) { for (int k = 0; k < d3; k++) { vector[d3 * d2 * i + d3 * j + k] = hist[i][j][k]; } } } return vector; } static ArrayList<double[][][]> getOpticalHistograms(Path filename, int w_d, int h_d, int o_d) throws PoTException{ ArrayList<double[][][]> histograms = new ArrayList<double[][][]>(); VideoCapture capture = new VideoCapture(filename.toString()); if (!capture.isOpened()) { LOG.warning("video file " + filename.getFileName() + " could not be opened."); double[][][] hist = new double[w_d][h_d][o_d]; histograms.add(hist); } else { // variables for processing images Mat original_frame = new Mat(); Mat frame = new Mat(); Mat frame_gray = new Mat(); Mat prev_frame_gray = new Mat(); MatOfPoint2f flow = new MatOfPoint2f(); // computing a list of histogram of optical flows (i.e. a list of 5*5*8 // arrays) for (int frame_index = 0;; frame_index++) { // capturing the video images capture.read(original_frame); if (original_frame.empty()) { if (frame_index == 0) { throw new PoTException("Could not read the video file"); } else break; } } else { // resizing the captured frame and converting it to the gray scale // image. Imgproc.resize(original_frame, frame, new Size(frame_width, frame_height)); Imgproc.cvtColor(frame, frame_gray, Imgproc.COLOR_BGR2GRAY); double[][][] hist = new double[w_d][h_d][o_d]; histograms.add(hist); // from frame #2 if (frame_index > 0) { // calculate optical flows Video.calcOpticalFlowFarneback(prev_frame_gray, frame_gray, flow, 0.5, 1, 10, 2, 7, 1.5, 0); // 0.5, 1, 15, 2, 7, 1.5, 0 // update histogram of optical flows updateOpticalHistogram(histograms.get(frame_index), flow); } Mat temp_frame = prev_frame_gray; prev_frame_gray = frame_gray; frame_gray = temp_frame; } } capture.release(); } return histograms; } static void updateOpticalHistogram(double[][][] hist, Mat flow) { int d1 = hist.length; int d2 = hist[0].length; int d3 = hist[0][0].length; int step = 4; // 5; for (int x = 0; x < frame_width; x += step) { int x_type = (int) (x * d1 / frame_width); for (int y = 0; y < frame_height; y += step) { int y_type = (int) (y * d2 / frame_height); Point fxy = new Point(flow.get(y, x)); double size = (fxy.x + fxy.y) * (fxy.x + fxy.y); if (size < 9) { continue; // 25 } else { int f_type = opticalFlowType(fxy, d3); hist[x_type][y_type][f_type]++; } } } } static int opticalFlowType(Point fxy, int dim) { double degree = Math.atan2(fxy.y, fxy.x); int type = 7; for (int i = 0; i < dim; i++) { double boundary = (i + 1) * 2 * Math.PI / dim - Math.PI; if (degree < boundary) { type = i; break; } } return type; } public static void saveVectors(double[][] vectors, Path outfile) { int d = vectors[0].length; ArrayList<double[][][]> temp_hists = new ArrayList<double[][][]>(); for (int i = 0; i < vectors.length; i++) { double[][][] temp_hist = new double[1][1][d]; temp_hist[0][0] = vectors[i]; temp_hists.add(temp_hist); } saveHistograms(temp_hists, outfile); } static void saveHistograms(ArrayList<double[][][]> hists, Path outfile) { int w_d = hists.get(0).length; int h_d = hists.get(0)[0].length; int o_d = hists.get(0)[0][0].length; int i, j, k, l; try (FileOutputStream fos = new FileOutputStream(outfile.toFile()); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos))) { String head = String.format("%d %d", hists.size(), w_d * h_d * o_d); writer.write(head); writer.newLine(); for (l = 0; l < (int) hists.size(); l++) { double[][][] hist = hists.get(l); for (i = 0; i < hist.length; i++) { for (j = 0; j < hist[0].length; j++) { for (k = 0; k < hist[0][0].length; k++) { // optical_bins+1 writer.write(String.format("%f ", hist[i][j][k])); } } } writer.newLine(); } } catch (IOException x) { System.err.println(x); } } public static double[][] loadTimeSeries(Path filename) { double[][] series = new double[1][1]; try (InputStream in = Files.newInputStream(filename); BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { Scanner scin = new Scanner(in); int num_frames = scin.nextInt(); int dim = scin.nextInt(); series = new double[num_frames][dim]; for (int i = 0; i < num_frames; i++) { for (int j = 0; j < dim; j++) { series[i][j] = scin.nextDouble(); } } } catch (IOException e) { e.printStackTrace(); } return series; } public static double[][] getGradientTimeSeries(Path filename, int w_d, int h_d, int o_d) throws PoTException { ArrayList<double[][][]> hists = getGradientHistograms(filename, w_d, h_d, o_d); double[][] vectors = new double[hists.size()][]; for (int i = 0; i < hists.size(); i++) { vectors[i] = histogramToVector(hists.get(i)); } return vectors; } static ArrayList<double[][][]> getGradientHistograms(Path filename, int w_d, int h_d, int o_d) throws PoTException { ArrayList<double[][][]> histograms = new ArrayList<double[][][]>(); VideoCapture capture = new VideoCapture(filename.toString()); if (!capture.isOpened()) { LOG.warning("video file not opened."); double[][][] hist = new double[w_d][h_d][o_d]; histograms.add(hist); } else { // variables for processing images Mat original_frame = new Mat(); Mat resized = new Mat(); Mat resized_gray = new Mat(); // initializing a list of histogram of gradients (i.e. a list of s*s*9 // arrays) for (int i = 0;; i++) { // capturing the video images capture.read(original_frame); if (original_frame.empty()) { if (original_frame.empty()) { if (i == 0) { throw new PoTException("Could not read the video file"); } else break; } } double[][][] hist = new double[w_d][h_d][o_d]; Imgproc.resize(original_frame, resized, new Size(frame_width, frame_height)); Imgproc.cvtColor(resized, resized_gray, Imgproc.COLOR_BGR2GRAY); ArrayList<double[][]> gradients = computeGradients(resized_gray, o_d); updateGradientHistogram(hist, gradients); histograms.add(hist); } capture.release(); } return histograms; } static ArrayList<double[][]> computeGradients(Mat frame, int dim) { byte frame_array[] = new byte[(int) frame.total()]; frame.get(0, 0, frame_array); ArrayList<double[][]> gradients = new ArrayList<double[][]>(); for (int k = 0; k < dim; k++) { double angle = Math.PI * (double) k / (double) dim; double dx = Math.cos(angle) * 0.9999999; double dy = Math.sin(angle) * 0.9999999; double[][] grad = new double[frame.width()][frame.height()]; for (int i = 0; i < frame.cols(); i++) { for (int j = 0; j < frame.rows(); j++) { if (i <= 1 || j <= 1 || i >= frame.cols() - 2 || j >= frame.rows() - 2) { grad[i][j] = 0; } else { double f1 = interpolatePixel(frame_array, frame.cols(), (double) i + dx, (double) j + dy); double f2 = interpolatePixel(frame_array, frame.cols(), (double) i - dx, (double) j - dy); double diff = f1 - f2; if (diff < 0) diff = diff * -1; if (diff >= 256) diff = 255; grad[i][j] = diff; } } } gradients.add(grad); } return gradients; } static double interpolatePixel(byte[] image, int w, double x, double y) { double x1 = (double) ((int) x); double x2 = (double) ((int) x + 1); double y1 = (double) ((int) y); double y2 = (double) ((int) y + 1); double f11 = (double) (image[(int) y * w + (int) x] & 0xFF); double f21 = (double) (image[(int) y * w + (int) x + 1] & 0xFF); double f12 = (double) (image[(int) (y + 1) * w + (int) x] & 0xFF); double f22 = (double) (image[(int) (y + 1) * w + (int) x + 1] & 0xFF); double f = f11 * (x2 - x) * (y2 - y) + f21 * (x - x1) * (y2 - y) + f12 * (x2 - x) * (y - y1) + f22 * (x - x1) * (y - y1); return f; } static void updateGradientHistogram(double[][][] hist, ArrayList<double[][]> gradients) { int d1 = hist.length; int d2 = hist[0].length; int d3 = hist[0][0].length; int width = gradients.get(0).length; int height = gradients.get(0)[0].length; for (int i = 0; i < width; i++) { int s1_index = i * d1 / width; for (int j = 0; j < height; j++) { int s2_index = j * d2 / height; for (int k = 0; k < d3; k++) { double val = gradients.get(k)[i][j] / 100; hist[s1_index][s2_index][k] += val; } } } } public static ArrayList<double[]> getTemporalWindows(int level) { ArrayList<double[]> fws = new ArrayList<double[]>(); for (int l = 0; l < level; l++) { int cascade_steps = (int) Math.pow((double) 2, (double) l);// 2; double step_size = (double) 1 / (double) cascade_steps; for (int k = 0; k < cascade_steps; k++) { double start = step_size * (double) k + 0.000001; double end = step_size * (double) (k + 1) + 0.000001; double[] wind = new double[2]; wind[0] = start; wind[1] = end; fws.add(wind); } } return fws; } public static ArrayList<Double> computeFeaturesFromSeries(double[][] series, ArrayList<double[]> time_windows_list, int feature_mode) { int start = 0; int end = series.length - 1; ArrayList<Double> feature = new ArrayList<Double>(); for (int j = 0; j < time_windows_list.size(); j++) { int duration = end - start; for (int i = 0; i < series[0].length; i++) { if (duration < 0) { if (feature_mode == 2 || feature_mode == 4) { feature.add(0.0); feature.add(0.0); } else feature.add(0.0); continue; } int window_start = start + (int) (duration * time_windows_list.get(j)[0] + 0.5); int window_end = start + (int) (duration * time_windows_list.get(j)[1] + 0.5); if (feature_mode == 1) { // Sum pooling double sum = 0; for (int t = window_start; t <= window_end; t++) { if (t < 0) continue; sum += series[t][i]; } feature.add(sum); } else if (feature_mode == 2) { // Gradient pooling1 double positive_gradients = 0; double negative_gradients = 0; for (int t = window_start; t <= window_end; t++) { int look = 2; if (t - look < 0) continue; else { double dif = series[t][i] - series[t - look][i]; if (dif > 0.01) { // 0.01 for optical positive_gradients++; } else if (dif < -0.01) { // if (dif<-10) negative_gradients++; } } } feature.add(positive_gradients); feature.add(negative_gradients); } else if (feature_mode == 4) { // Gradient pooling2 double positive_gradients = 0; double negative_gradients = 0; for (int t = window_start; t <= window_end; t++) { int look = 2; if (t - look < 0) continue; else { double dif = series[t][i] - series[t - look][i]; if (dif > 0) { positive_gradients += dif; } else { negative_gradients += -dif; } } } feature.add(positive_gradients); feature.add(negative_gradients); } else if (feature_mode == 5) { // Max pooling double max = -1000000; for (int t = window_start; t <= window_end; t++) { if (t < 0) continue; if (series[t][i] > max) max = series[t][i]; } feature.add(max); } } } return feature; } public static void normalizeFeatureL1(ArrayList<Double> sample) { int sum = 0; for (int i = 0; i < sample.size(); i++) { double val = sample.get(i); if (val < 0) val = -1 * val; sum += val; } for (int i = 0; i < sample.size(); i++) { double v; if (sum == 0) v = 0; else v = sample.get(i) / sum;// *100; sample.set(i, v); } } static double chiSquareDistance(ArrayList<Double> feature1, ArrayList<Double> feature2) { if (feature1.size() != feature2.size()) LOG.warning("feature vector dimension mismatch."); double score = 0; for (int i = 0; i < feature1.size(); i++) { double h1 = feature1.get(i); double h2 = feature2.get(i); if (h1 < 0 || h2 < 0) { LOG.warning("A negative feature value. The chi square kernel " + "does not work with negative values. Please try shifting " + "the vector to make all its elements positive."); } if (h1 == h2) continue; else score += (h1 - h2) * (h1 - h2) / (h1 + h2); } return 0.5 * score; } static double meanChiSquareDistances(ArrayList<FeatureVector> samples, int d) { double mean_dist = 0; double sum = 0; int count = 0; for (int i = 0; i < samples.size(); i++) { for (int j = i + 1; j < samples.size(); j++) { count++; sum += chiSquareDistance(samples.get(i).feature.get(d), samples.get(j).feature.get(d)); } } mean_dist = sum / (double) count; return mean_dist; } static double kernelDistance(FeatureVector sample1, FeatureVector sample2, double[] mean_dists) { double distance = 0; for (int d = 0; d < sample1.numDim(); d++) { double weight = 1; double val = chiSquareDistance(sample1.feature.get(d), sample2.feature.get(d)) / mean_dists[d] * weight; if (mean_dists[d] == 0) val = chiSquareDistance(sample1.feature.get(d), sample2.feature.get(d)) / 1000000.0; distance = distance + val; } double final_score = Math.exp(-1 * distance / 10); // 10000 10 return final_score; } }