View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.math4.examples.kmeans.image;
19  
20  import java.util.concurrent.Callable;
21  import java.time.Instant;
22  import java.time.Duration;
23  
24  import picocli.CommandLine;
25  import picocli.CommandLine.Option;
26  import picocli.CommandLine.Command;
27  
28  import org.apache.commons.rng.UniformRandomProvider;
29  import org.apache.commons.rng.simple.RandomSource;
30  import org.apache.commons.math4.legacy.ml.distance.DistanceMeasure;
31  import org.apache.commons.math4.legacy.ml.distance.EuclideanDistance;
32  import org.apache.commons.math4.legacy.ml.clustering.Clusterer;
33  import org.apache.commons.math4.legacy.ml.clustering.KMeansPlusPlusClusterer;
34  import org.apache.commons.math4.legacy.ml.clustering.ElkanKMeansPlusPlusClusterer;
35  
36  /**
37   * Application class.
38   */
39  @Command(description = "Run the application",
40           mixinStandardHelpOptions = true)
41  public final class StandAlone implements Callable<Void> {
42      /** The k parameter. */
43      @Option(names = { "-k" }, paramLabel = "K", required = true,
44              description = "Number of clusters.")
45      private int numClusters;
46      /** The maximal number of iterations. */
47      @Option(names = { "-i", "--iterations" }, paramLabel = "N",
48              description = "Allowed number of iterations (default: ${DEFAULT-VALUE}).")
49      private int maxIter = 2000;
50      /** The input file. */
51      @Option(names = { "--image" }, paramLabel = "FILE", required = true,
52              description = "Input file name.")
53      private String inputFile;
54      /** The output prefix. */
55      @Option(names = { "-o", "--output" }, paramLabel = "PREFIX", required = true,
56              description = "Prefix (path) for the output files.")
57      private String outputPrefix;
58  
59      /**
60       * Program entry point.
61       *
62       * @param args Command line arguments and options.
63       */
64      public static void main(String[] args) {
65          CommandLine.call(new StandAlone(), args);
66      }
67  
68      @Override
69      public Void call() {
70          final ImageData image = ImageData.load(inputFile);
71          final UniformRandomProvider rng = RandomSource.MWC_256.create();
72          final DistanceMeasure distance = new EuclideanDistance();
73  
74          cluster(image,
75                  new ElkanKMeansPlusPlusClusterer<>(numClusters,
76                                                     maxIter,
77                                                     distance,
78                                                     rng),
79                  "elkan");
80          cluster(image,
81                  new KMeansPlusPlusClusterer<>(numClusters,
82                                                maxIter,
83                                                distance,
84                                                rng),
85                  "kmeans");
86  
87          return null;
88      }
89  
90      /**
91       * Perform clustering and write results.
92       *
93       * @param image Input.
94       * @param algo Algorithm to do the clustering.
95       * @param id Identifier for output file name.
96       */
97      private void cluster(ImageData image,
98                           Clusterer<ImageData.PixelClusterable> algo,
99                           String id) {
100         final String dot = ".";
101         final String out = new StringBuilder()
102             .append(outputPrefix)
103             .append(dot)
104             .append("k_")
105             .append(numClusters)
106             .append(dot)
107             .append(id)
108             .append(dot)
109             .toString();
110 
111         final Instant start = Instant.now();
112         image.write(algo.cluster(image.getPixels()), out);
113         //CHECKSTYLE: stop all
114         System.out.println("time=" + Duration.between(start, Instant.now()).toMillis());
115         //CHECKSTYLE: resume all
116     }
117 }