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.io.File;
21  import java.io.IOException;
22  import java.io.UncheckedIOException;
23  import java.util.List;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.awt.image.BufferedImage;
28  import java.awt.image.Raster;
29  import java.awt.image.WritableRaster;
30  import org.apache.commons.imaging.Imaging;
31  import org.apache.commons.imaging.ImageFormat;
32  import org.apache.commons.imaging.ImageFormats;
33  import org.apache.commons.imaging.ImageReadException;
34  import org.apache.commons.imaging.ImageWriteException;
35  import org.apache.commons.math4.legacy.ml.clustering.Clusterable;
36  import org.apache.commons.math4.legacy.ml.clustering.Cluster;
37  
38  /**
39   * Retrieve pixel contents from an image file.
40   */
41  final class ImageData {
42      /** Image data. */
43      private final Raster data;
44      /** Pixel dataset. */
45      private final List<PixelClusterable> pixels = new ArrayList<>();
46  
47      /**
48       * @param image Image.
49       */
50      private ImageData(BufferedImage image) {
51          data = image.getData();
52  
53          // Build dataset.
54          for (int row = 0; row < image.getHeight(); row++) {
55              for (int col = 0; col < image.getWidth(); col++) {
56                  pixels.add(new PixelClusterable(col, row));
57              }
58          }
59      }
60  
61      /**
62       * Load from file.
63       *
64       * @param file Graphics file.
65       * @return a new instance.
66       */
67      static ImageData load(String file) {
68          try {
69              return new ImageData(Imaging.getBufferedImage(new File(file)));
70          } catch (IOException e) {
71              throw new UncheckedIOException(e);
72          } catch (ImageReadException e) {
73              throw new RuntimeException(e);
74          }
75      }
76  
77      /**
78       * Create clustered image and write it to a file in PNG format.
79       *
80       * @param clusters Clusters.
81       * @param outputPrefix Prefix of the output file.
82       * Graphics format extension will be appended.
83       */
84      void write(List<? extends Cluster<PixelClusterable>> clusters,
85                 String outputPrefix) {
86          final BufferedImage imageC = new BufferedImage(data.getWidth(),
87                                                         data.getHeight(),
88                                                         BufferedImage.TYPE_INT_RGB);
89  
90          final WritableRaster raster = imageC.getRaster();
91  
92          for (Cluster<PixelClusterable> cluster : clusters) {
93              final double[] color = cluster.centroid().getPoint();
94              for (PixelClusterable pixel : cluster.getPoints()) {
95                  raster.setPixel(pixel.x, pixel.y, color);
96              }
97          }
98  
99          try {
100             final ImageFormat format = ImageFormats.PNG;
101             Imaging.writeImage(imageC,
102                                new File(outputPrefix + format.getDefaultExtension()),
103                                format);
104         } catch (IOException e) {
105             throw new UncheckedIOException(e);
106         } catch (ImageWriteException e) {
107             throw new RuntimeException(e);
108         }
109     }
110 
111     /**
112      * @return the dataset.
113      */
114     Collection<PixelClusterable> getPixels() {
115         return Collections.unmodifiableCollection(pixels);
116     }
117 
118     /**
119      * Bridge that presents a pixel as clusterable data.
120      * Instances are mutable; they keep a reference to the original
121      * image data.
122      */
123     class PixelClusterable implements Clusterable {
124         /** Pixel abscissa. */
125         private final int x;
126         /** Pixel ordinate. */
127         private final int y;
128         /** Pixel color. */
129         private double[] color;
130 
131         /**
132          * @param x Abscissa.
133          * @param y Ordinate.
134          */
135         PixelClusterable(int x,
136                          int y) {
137             this.x = x;
138             this.y = y;
139             this.color = null;
140         }
141 
142         /** {@inheritDoc} */
143         @Override
144         public double[] getPoint() {
145             if (color == null) {
146                 color = data.getPixel(x, y, (double[]) null);
147             }
148             return color;
149         }
150     }
151 }